| @@ -37,8 +37,9 @@ | ||
| 37 | 37 | * Get paginated parent comments with users, images, and replies preloaded. |
| 38 | 38 | */ |
| 39 | 39 | public function getComments($id, $per_page, $filter, $boardId = null) |
| 40 | 40 | { |
| 41 | + $per_page = Sanitize::perPage($per_page); | |
| 41 | 42 | $task = $boardId ? (new TaskService())->findTaskOnBoard($id, $boardId) : Task::findOrFail($id); |
| 42 | 43 | |
| 43 | 44 | $commentsQuery = $task->comments()->whereNull('parent_id') |
| 44 | 45 | ->with(['user', 'images', 'replies.user', 'replies.images']); |
| @@ -94,9 +95,9 @@ | ||
| 94 | 95 | do_action('fluent_boards/comment_created', $comment); |
| 95 | 96 | return $comment; |
| 96 | 97 | } |
| 97 | 98 | |
| 98 | - /** Allow only paragraph content and the comment editor's five inline formats. */ | |
| 99 | + /** Preserve comment formatting while stripping executable HTML and unsafe attributes. */ | |
| 99 | 100 | public function sanitizeContent($content) |
| 100 | 101 | { |
| 101 | 102 | if (!is_string($content) || trim(wp_strip_all_tags($content)) === '') { |
| 102 | 103 | return ''; |
| @@ -105,8 +106,11 @@ | ||
| 105 | 106 | return trim(wp_kses($content, [ |
| 106 | 107 | 'p' => [], 'br' => [], 'strong' => [], 'b' => [], |
| 107 | 108 | 'em' => [], 'i' => [], 'del' => [], 's' => [], 'code' => [], |
| 108 | 109 | 'a' => ['href' => true, 'title' => true], |
| 110 | + 'ul' => [], 'ol' => ['start' => true], 'li' => [], | |
| 111 | + 'h1' => [], 'h2' => [], 'h3' => [], 'h4' => [], 'h5' => [], 'h6' => [], | |
| 112 | + 'blockquote' => [], 'pre' => ['data-language' => true], | |
| 109 | 113 | ])); |
| 110 | 114 | } |
| 111 | 115 | |
| 112 | 116 | /** Resolve mentions and bare URLs in text without rewriting link attributes or code. */ |
| @@ -114,9 +118,9 @@ | ||
| 114 | 118 | { |
| 115 | 119 | $parts = wp_html_split($this->sanitizeContent($content)); |
| 116 | 120 | $skipDepth = 0; |
| 117 | 121 | foreach ($parts as &$part) { |
| 118 | - if (preg_match('~^</?(a|code)\b~i', $part)) { | |
| 122 | + if (preg_match('~^</?(a|code|pre)\b~i', $part)) { | |
| 119 | 123 | $skipDepth += strpos($part, '</') === 0 ? -1 : 1; |
| 120 | 124 | $skipDepth = max(0, $skipDepth); |
| 121 | 125 | } elseif ($skipDepth === 0 && $part !== '' && $part[0] !== '<') { |
| 122 | 126 | $part = $this->processMentionAndLink($part, $mentionData); |