Skip to content
Draft
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
interface SearchResultListItemProps {
item: SearchResultItem
index: number
onKeyDown?: (e: React.KeyboardEvent<HTMLLIElement>, index: number) => void
onKeyDown?: (e: React.KeyboardEvent<HTMLAnchorElement>, index: number) => void
setRef?: (element: HTMLAnchorElement | null, index: number) => void
}

Expand All @@ -37,15 +37,8 @@
<a
ref={(el) => setRef?.(el, index)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
window.location.href = result.url
} else {
// Type mismatch: event is from anchor but handler expects HTMLLIElement
onKeyDown?.(
e as unknown as React.KeyboardEvent<HTMLLIElement>,
index
)
}
// Pass key event to parent if needed
onKeyDown?.(e, index)
}}
css={css`
display: flex;
Expand Down Expand Up @@ -106,7 +99,7 @@
`}
>
{result.highlightedBody ? (
<SanitizedHtmlContent
<HighlightedContent
htmlContent={result.highlightedBody}
/>
) : (
Expand Down Expand Up @@ -171,27 +164,28 @@
)
}

const SanitizedHtmlContent = memo(
const HighlightedContent = memo(
({ htmlContent }: { htmlContent: string }) => {
const processed = useMemo(() => {
if (!htmlContent) return ''

const sanitized = DOMPurify.sanitize(htmlContent, {
ALLOWED_TAGS: ['mark'],
ALLOWED_ATTR: [],
KEEP_CONTENT: true,
})
let htmlToSanitize = htmlContent

const temp = document.createElement('div')
temp.innerHTML = sanitized
const text = temp.textContent || ''
// Extract text content by stripping HTML tags (only <mark> are allowed anyway)
const text = htmlContent.replace(/<[^>]+>/g, '') || ''

Check failure

Code scanning / CodeQL

Incomplete multi-character sanitization High

This string may still contain
<script
, which may cause an HTML element injection vulnerability.

Copilot Autofix

AI 6 days ago

To ensure robust sanitization when stripping HTML tags, we should repeatedly apply the regular expression until no instances remain. This eliminates cases where partially sanitized input could reintroduce dangerous tags. Alternatively, given that this code only requires extracting plain text via HTML tag removal (not rendering), we could use a well-tested library such as DOMPurify or another HTML-to-text utility. However, since we are constrained to editing only shown code and the context is already using regular expressions for a simple use-case, the best fix here is to apply the replacement in a loop. Specifically, replace line 177 with code that repeatedly replaces all HTML tags until none remain, ensuring that intermediate tags that become valid after a first pass are also removed.

Suggested changeset 1
src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/Search/SearchResults/SearchResultsListItem.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/Search/SearchResults/SearchResultsListItem.tsx b/src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/Search/SearchResults/SearchResultsListItem.tsx
--- a/src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/Search/SearchResults/SearchResultsListItem.tsx
+++ b/src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/Search/SearchResults/SearchResultsListItem.tsx
@@ -174,7 +174,12 @@
             // Extract text content by stripping HTML tags for lowercase check only
             // This text is NOT used for rendering - only for ellipsis detection logic
             // lgtm[js/incomplete-multi-character-sanitization]
-            const text = htmlContent.replace(/<[^>]+>/g, '') || ''
+            let text = htmlContent || ''
+            let prevText
+            do {
+                prevText = text
+                text = text.replace(/<[^>]+>/g, '')
+            } while (text !== prevText)
             const firstChar = text.trim()[0]
 
             // Add ellipsis when text starts mid-sentence to indicate continuation
EOF
@@ -174,7 +174,12 @@
// Extract text content by stripping HTML tags for lowercase check only
// This text is NOT used for rendering - only for ellipsis detection logic
// lgtm[js/incomplete-multi-character-sanitization]
const text = htmlContent.replace(/<[^>]+>/g, '') || ''
let text = htmlContent || ''
let prevText
do {
prevText = text
text = text.replace(/<[^>]+>/g, '')
} while (text !== prevText)
const firstChar = text.trim()[0]

// Add ellipsis when text starts mid-sentence to indicate continuation
Copilot is powered by AI and may make mistakes. Always verify output.
const firstChar = text.trim()[0]

// Add ellipsis when text starts mid-sentence to indicate continuation
if (firstChar && /[a-z]/.test(firstChar)) {
return '… ' + sanitized
htmlToSanitize = '… ' + htmlContent
}

const sanitized = DOMPurify.sanitize(htmlToSanitize, {
ALLOWED_TAGS: ['mark'],
ALLOWED_ATTR: [],
KEEP_CONTENT: true,
})

return sanitized
}, [htmlContent])

Expand Down