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', $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 @@ >