diff --git a/public_html/wp-content/plugins/pattern-directory/includes/admin-flags.php b/public_html/wp-content/plugins/pattern-directory/includes/admin-flags.php
index d34d2686..379a3b10 100644
--- a/public_html/wp-content/plugins/pattern-directory/includes/admin-flags.php
+++ b/public_html/wp-content/plugins/pattern-directory/includes/admin-flags.php
@@ -171,7 +171,7 @@ function flag_list_table_row_actions( $actions, $post ) {
$actions['review'] = sprintf(
'%s',
- esc_attr( $pattern_url ),
+ esc_url( $pattern_url ),
/* translators: %s: Post title. */
esc_attr( sprintf( __( 'Review “%s”', 'wporg-patterns' ), $pattern_title ) ),
__( 'Review Pattern', 'wporg-patterns' )
@@ -188,7 +188,7 @@ function flag_list_table_row_actions( $actions, $post ) {
$actions['resolve'] = sprintf(
'%s',
- esc_attr( $resolve_url ),
+ esc_url( $resolve_url ),
esc_attr( __( 'Mark this flag as resolved', 'wporg-patterns' ) ),
__( 'Resolve', 'wporg-patterns' )
);
@@ -205,7 +205,7 @@ function flag_list_table_row_actions( $actions, $post ) {
$actions['unresolve'] = sprintf(
'%s',
- esc_attr( $unresolve_url ),
+ esc_url( $unresolve_url ),
esc_attr( __( 'Mark this flag as pending', 'wporg-patterns' ) ),
__( 'Unresolve', 'wporg-patterns' )
);
@@ -225,7 +225,7 @@ function flag_list_table_row_actions( $actions, $post ) {
$actions['view-all'] = sprintf(
'
%s',
- esc_attr( $view_all_url ),
+ esc_url( $view_all_url ),
/* translators: %s: Post title. */
esc_attr( sprintf( __( 'View all flags for “%s”', 'wporg-patterns' ), $pattern_title ) ),
__( 'View All Flags For This Pattern', 'wporg-patterns' )
diff --git a/public_html/wp-content/plugins/pattern-directory/includes/admin-patterns.php b/public_html/wp-content/plugins/pattern-directory/includes/admin-patterns.php
index 18e508b1..f9927b01 100644
--- a/public_html/wp-content/plugins/pattern-directory/includes/admin-patterns.php
+++ b/public_html/wp-content/plugins/pattern-directory/includes/admin-patterns.php
@@ -436,7 +436,7 @@ function add_row_actions( $actions, $post ) {
$actions['publish'] = sprintf(
'%s',
- $publish_url,
+ esc_url( $publish_url ),
/* translators: %s: Post title. */
esc_attr( sprintf( __( 'Publish “%s”', 'wporg-patterns' ), $title ) ),
_x( 'Publish', 'verb', 'wporg-patterns' )
@@ -454,7 +454,7 @@ function add_row_actions( $actions, $post ) {
$actions['unlist'] = sprintf(
'%s',
- $unlist_url,
+ esc_url( $unlist_url ),
/* translators: %s: Post title. */
esc_attr( sprintf( __( 'Remove “%s” from the directory', 'wporg-patterns' ), $title ) ),
_x( 'Unlist', 'verb', 'wporg-patterns' )
@@ -472,7 +472,7 @@ function add_row_actions( $actions, $post ) {
$actions['spam'] = sprintf(
'%s',
- $spam_url,
+ esc_url( $spam_url ),
/* translators: %s: Post title. */
esc_attr( sprintf( __( 'Mark “%s” as spam', 'wporg-patterns' ), $title ) ),
_x( 'Spam', 'verb', 'wporg-patterns' )
diff --git a/public_html/wp-content/plugins/pattern-directory/includes/pattern-flag-post-type.php b/public_html/wp-content/plugins/pattern-directory/includes/pattern-flag-post-type.php
index 9760490f..3eefc4e4 100644
--- a/public_html/wp-content/plugins/pattern-directory/includes/pattern-flag-post-type.php
+++ b/public_html/wp-content/plugins/pattern-directory/includes/pattern-flag-post-type.php
@@ -186,24 +186,29 @@ function get_pattern_ids_with_pending_flags( $args = array() ) {
$pattern = PATTERN;
$flag = POST_TYPE;
- // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
- // phpcs:ignore WordPress.DB.DirectDatabaseQuery
+ // Allowlist orderby/order; these are a column and a keyword, which can't be bound as placeholders.
+ $orderby_columns = array(
+ 'date' => 'patterns.post_date',
+ 'title' => 'patterns.post_title',
+ 'id' => 'patterns.ID',
+ );
+ $orderby_key = is_string( $args['orderby'] ) ? strtolower( $args['orderby'] ) : '';
+ $orderby = $orderby_columns[ $orderby_key ] ?? 'patterns.post_date';
+ $order = ( is_string( $args['order'] ) && 'asc' === strtolower( $args['order'] ) ) ? 'ASC' : 'DESC';
+
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery
$pattern_ids = $wpdb->get_col(
- $wpdb->prepare(
- "
- SELECT DISTINCT patterns.ID
- FROM {$wpdb->posts} patterns
- JOIN {$wpdb->posts} flags ON patterns.ID = flags.post_parent
- AND flags.post_type = '{$flag}'
- AND flags.post_status = 'pending'
- WHERE patterns.post_type = '{$pattern}'
- ORDER BY %s %s
- ",
- $args['orderby'],
- $args['order']
- )
+ "
+ SELECT DISTINCT patterns.ID
+ FROM {$wpdb->posts} patterns
+ JOIN {$wpdb->posts} flags ON patterns.ID = flags.post_parent
+ AND flags.post_type = '{$flag}'
+ AND flags.post_status = 'pending'
+ WHERE patterns.post_type = '{$pattern}'
+ ORDER BY {$orderby} {$order}
+ "
);
- // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery
return $pattern_ids;
}
diff --git a/public_html/wp-content/plugins/pattern-directory/includes/pattern-post-type.php b/public_html/wp-content/plugins/pattern-directory/includes/pattern-post-type.php
index 54737d8a..3a7f4862 100644
--- a/public_html/wp-content/plugins/pattern-directory/includes/pattern-post-type.php
+++ b/public_html/wp-content/plugins/pattern-directory/includes/pattern-post-type.php
@@ -789,12 +789,25 @@ function filter_patterns_rest_query( $args, $request ) {
$allowed_blocks = $request->get_param( 'allowed_blocks' );
if ( $allowed_blocks ) {
- // Only return a pattern if all contained blocks are in the allowed blocks list.
- $args['meta_query']['allowed_blocks'] = array(
- 'key' => 'wpop_contains_block_types',
- 'compare' => 'REGEXP',
- 'value' => '^((' . implode( '|', $allowed_blocks ) . '),?)+$',
+ // Restrict to valid block names so arbitrary regex can't be injected into the REGEXP compare below.
+ $allowed_blocks = array_filter(
+ (array) $allowed_blocks,
+ function ( $block_name ) {
+ return is_string( $block_name ) && preg_match( '#^[a-z0-9-]+/[a-z0-9-]+$#', $block_name );
+ }
);
+
+ if ( $allowed_blocks ) {
+ // Only return patterns whose blocks are all in the allowed list.
+ $args['meta_query']['allowed_blocks'] = array(
+ 'key' => 'wpop_contains_block_types',
+ 'compare' => 'REGEXP',
+ 'value' => '^((' . implode( '|', $allowed_blocks ) . '),?)+$',
+ );
+ } else {
+ // Every requested block name was invalid; match no patterns.
+ $args['post__in'] = array( 0 );
+ }
}
return $args;
diff --git a/public_html/wp-content/plugins/pattern-translations/includes/parsers/BasicText.php b/public_html/wp-content/plugins/pattern-translations/includes/parsers/BasicText.php
index f3af3488..e080500c 100644
--- a/public_html/wp-content/plugins/pattern-translations/includes/parsers/BasicText.php
+++ b/public_html/wp-content/plugins/pattern-translations/includes/parsers/BasicText.php
@@ -46,7 +46,7 @@ public function replace_strings( array $block, array $replacements ): array {
foreach ( $text_nodes as $text ) {
if ( trim( $text->nodeValue ) && isset( $replacements[ $text->nodeValue ] ) ) {
$regex = '#(<([^>]*)>)?' . preg_quote( $text->nodeValue, '/' ) . '(<([^>]*)>)?#is';
- $inner_content = preg_replace( $regex, '${1}' . $replacements[ $text->nodeValue ] . '${3}', $inner_content );
+ $inner_content = preg_replace( $regex, '${1}' . addcslashes( $replacements[ $text->nodeValue ], '\\$' ) . '${3}', $inner_content );
}
}
}
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php
index 1f0419e0..8be9ad03 100644
--- a/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php
+++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php
@@ -469,7 +469,9 @@ function update_archive_title( $block_content, $block, $instance ) {
$title = __( 'Search results', 'wporg-patterns' );
}
+ $allowed_tags = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' );
$tag_name = isset( $attributes['level'] ) ? 'h' . (int) $attributes['level'] : 'h1';
+ $tag_name = in_array( $tag_name, $allowed_tags, true ) ? $tag_name : 'h1';
$align_class_name = empty( $attributes['textAlign'] ) ? '' : "has-text-align-{$attributes['textAlign']}";
// Required to prevent `block_to_render` from being null in `get_block_wrapper_attributes`.
@@ -482,7 +484,7 @@ function update_archive_title( $block_content, $block, $instance ) {
'<%1$s %2$s>%3$s%1$s>',
$tag_name,
$wrapper_attributes,
- $title
+ esc_html( $title )
);
}
return $block_content;
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php
index b9a43054..de99460c 100644
--- a/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php
+++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php
@@ -12,7 +12,7 @@
'pattern_edit_link',
function () {
$post_id = get_the_ID();
- return site_url( "pattern/$post_id/edit/" );
+ return esc_url( site_url( "pattern/$post_id/edit/" ) );
}
);
@@ -23,12 +23,14 @@ function () {
'pattern_draft_link',
function () {
$post_id = get_the_ID();
- return add_query_arg(
- array(
- 'action' => 'draft',
- '_wpnonce' => wp_create_nonce( 'draft-' . $post_id ),
- ),
- get_the_permalink()
+ return esc_url(
+ add_query_arg(
+ array(
+ 'action' => 'draft',
+ '_wpnonce' => wp_create_nonce( 'draft-' . $post_id ),
+ ),
+ get_the_permalink()
+ )
);
}
);
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php
index 358c5711..30436a98 100644
--- a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php
+++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php
@@ -34,7 +34,7 @@
>