You've already forked wp-bootstrap
Security audit fixes: regex hardening, performance, and code quality (v1.1.2)
- WidgetRenderer: single regex for h2→h4 prevents mismatched tags - ContextBuilder: O(n) comment tree with parent-indexed lookup map - ContextBuilder: consolidated sidebar queries into single check - ContextBuilder: transient caching for sidebar recent posts and tags - functions.php: hex-to-RGB consolidation, type hints, ctype_xdigit validation - Transient invalidation hooks for save_post and tag CRUD operations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -58,7 +58,9 @@ class ContextBuilder
|
||||
$context['search_query'] = get_search_query();
|
||||
}
|
||||
|
||||
// Sidebar layout detection.
|
||||
// Sidebar: determine once whether the current page needs sidebar data.
|
||||
$needsSidebar = false;
|
||||
|
||||
if (is_home()) {
|
||||
$pageId = (int) get_option('page_for_posts');
|
||||
if ($pageId) {
|
||||
@@ -67,19 +69,14 @@ class ContextBuilder
|
||||
$context['layout'] = 'sidebar';
|
||||
}
|
||||
}
|
||||
$context['sidebar'] = $this->getSidebarData();
|
||||
$needsSidebar = true;
|
||||
} elseif (is_singular('post')) {
|
||||
$needsSidebar = true;
|
||||
} elseif (is_page() && get_page_template_slug() === 'page-sidebar') {
|
||||
$needsSidebar = true;
|
||||
}
|
||||
|
||||
// Sidebar data for pages using the "Page with Sidebar" template.
|
||||
if (is_page()) {
|
||||
$slug = get_page_template_slug();
|
||||
if ($slug === 'page-sidebar') {
|
||||
$context['sidebar'] = $this->getSidebarData();
|
||||
}
|
||||
}
|
||||
|
||||
// Posts always get sidebar data (sidebar is the default layout).
|
||||
if (is_singular('post')) {
|
||||
if ($needsSidebar) {
|
||||
$context['sidebar'] = $this->getSidebarData();
|
||||
}
|
||||
|
||||
@@ -309,21 +306,26 @@ class ContextBuilder
|
||||
|
||||
/**
|
||||
* Build a nested comment tree from flat comments.
|
||||
*
|
||||
* Uses a parent-indexed lookup map for O(n) performance instead of
|
||||
* scanning all comments at each recursion level (O(n^2)).
|
||||
*/
|
||||
private function buildCommentTree(array $comments, int $parentId = 0): array
|
||||
private function buildCommentTree(array $comments, int $parentId = 0, ?array $index = null): array
|
||||
{
|
||||
if ($index === null) {
|
||||
$index = [];
|
||||
foreach ($comments as $comment) {
|
||||
$parent = (int) $comment->comment_parent;
|
||||
$index[$parent][] = $comment;
|
||||
}
|
||||
}
|
||||
|
||||
$tree = [];
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
if ((int) $comment->comment_parent !== $parentId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($index[$parentId] ?? [] as $comment) {
|
||||
$tree[] = [
|
||||
'id' => (int) $comment->comment_ID,
|
||||
// Escape at source — comment_author is user-supplied, store as safe text.
|
||||
'author' => esc_html($comment->comment_author),
|
||||
// esc_url() strips dangerous schemes (javascript:, data:) and encodes for HTML.
|
||||
'author_url' => esc_url($comment->comment_author_url),
|
||||
'avatar_url' => get_avatar_url($comment, ['size' => 40]),
|
||||
'date' => get_comment_date('', $comment),
|
||||
@@ -336,7 +338,7 @@ class ContextBuilder
|
||||
'depth' => 1,
|
||||
'max_depth' => get_option('thread_comments_depth', 5),
|
||||
], $comment),
|
||||
'children' => $this->buildCommentTree($comments, (int) $comment->comment_ID),
|
||||
'children' => $this->buildCommentTree($comments, (int) $comment->comment_ID, $index),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -452,9 +454,17 @@ class ContextBuilder
|
||||
|
||||
/**
|
||||
* Get recent posts for sidebar.
|
||||
*
|
||||
* Cached via transient for 1 hour; invalidated on save_post.
|
||||
*/
|
||||
private function getSidebarRecentPosts(int $count = 4): array
|
||||
{
|
||||
$transient_key = 'wp_bootstrap_sidebar_recent_' . $count;
|
||||
$cached = get_transient($transient_key);
|
||||
if (false !== $cached) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$query = new \WP_Query([
|
||||
'posts_per_page' => $count,
|
||||
'orderby' => 'date',
|
||||
@@ -474,14 +484,23 @@ class ContextBuilder
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
set_transient($transient_key, $posts, HOUR_IN_SECONDS);
|
||||
return $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags for sidebar tag cloud.
|
||||
*
|
||||
* Cached via transient for 1 hour; invalidated on tag changes.
|
||||
*/
|
||||
private function getSidebarTags(int $count = 15): array
|
||||
{
|
||||
$transient_key = 'wp_bootstrap_sidebar_tags_' . $count;
|
||||
$cached = get_transient($transient_key);
|
||||
if (false !== $cached) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$tags = get_tags([
|
||||
'number' => $count,
|
||||
'orderby' => 'count',
|
||||
@@ -501,6 +520,7 @@ class ContextBuilder
|
||||
];
|
||||
}
|
||||
|
||||
set_transient($transient_key, $items, HOUR_IN_SECONDS);
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user