-
-
Notifications
You must be signed in to change notification settings - Fork 10.2k
fix potential issue when highlighting keyword #5526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughRefactors Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR addresses potential security and stability issues in the keyword highlighting functionality by adding HTML escaping, regex special character escaping, and error handling.
Key changes:
- Refactored
highlightKeywordfunction to escape HTML entities and prevent XSS vulnerabilities - Added
escapeHtmlhelper function to sanitize user input and matched text - Added
buildRegexhelper function with proper regex escaping and error handling
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| while ((match = regex.exec(fulltext)) !== null) { | ||
| hasMatch = true; | ||
| highlighted += escapeHtml(fulltext.substring(lastIndex, match.index)); | ||
| highlighted += '<span class="highlight" style="background: yellow;padding: 1px 4px;">' + escapeHtml(match[0]) + '</span>'; | ||
| lastIndex = match.index + match[0].length; | ||
| } |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex execution loop may lead to an infinite loop if the regex matches an empty string. When a regex matches an empty string, match.index + match[0].length equals match.index, causing lastIndex to not advance. Consider adding a check to increment lastIndex by 1 when an empty match occurs to prevent the infinite loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The review suggestion is technically correct for general regular expression loops, but it is not necessary in this specific context.
- Input Validation: The function highlightKeyword explicitly checks if (!keyword || keyword.length === 0) at line 265. This ensures the keyword is never an empty string.
- Regex Construction: The buildRegex function (lines 307-317) escapes all special regex characters in the keyword. This means the generated RegExp will only match the literal characters of the keyword.
- No Zero-Length Matches: Since the keyword is guaranteed to be non-empty and is treated as a literal string, any match found by regex.exec(fulltext) will have a length greater than 0 (match[0].length > 0).
- Loop Termination: Because match[0].length is always positive, lastIndex will always increment, guaranteeing that the while loop will eventually terminate.
Therefore, while the infinite loop risk exists for complex or user-supplied regex patterns that can match empty strings (e.g., a*), it is impossible with the logic currently implemented in this file.
| let lastIndex = 0; | ||
| let highlighted = ''; | ||
| let hasMatch = false; | ||
| let match; | ||
| while ((match = regex.exec(fulltext)) !== null) { | ||
| hasMatch = true; | ||
| highlighted += escapeHtml(fulltext.substring(lastIndex, match.index)); | ||
| highlighted += '<span class="highlight" style="background: yellow;padding: 1px 4px;">' + escapeHtml(match[0]) + '</span>'; | ||
| lastIndex = match.index + match[0].length; | ||
| } | ||
| highlighted += escapeHtml(fulltext.substring(lastIndex)); | ||
| return hasMatch ? highlighted : safeFulltext; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manual regex execution with while loop and state management adds complexity. Since JavaScript's String.replace() method with a function callback handles global replacements safely and automatically, consider simplifying this implementation by using fulltext.replace(regex, function(match) { return '<span class="highlight" style="background: yellow;padding: 1px 4px;">' + escapeHtml(match) + '</span>'; }). This would be more maintainable and avoid potential infinite loop issues.
19e03cd to
794350f
Compare
apollo-portal/src/main/resources/static/scripts/controller/GlobalSearchValueController.js
Show resolved
Hide resolved
apollo-portal/src/main/resources/static/scripts/controller/GlobalSearchValueController.js
Show resolved
Hide resolved
apollo-portal/src/main/resources/static/scripts/controller/GlobalSearchValueController.js
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apollo-portal/src/main/resources/static/scripts/controller/GlobalSearchValueController.js (1)
283-300: Consider using an object map for cleaner character mapping.The switch statement works correctly but could be simplified using an object literal for more concise code.
🔎 Optional refactor using object map
function escapeHtml(text) { - return (text || '').replace(/[&<>"']/g, function (char) { - switch (char) { - case '&': - return '&'; - case '<': - return '<'; - case '>': - return '>'; - case '"': - return '"'; - case "'": - return '''; - default: - return char; - } - }); + const escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return (text || '').replace(/[&<>"']/g, function (char) { + return escapeMap[char] || char; + }); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apollo-portal/src/main/resources/static/scripts/controller/GlobalSearchValueController.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build (8)
🔇 Additional comments (1)
apollo-portal/src/main/resources/static/scripts/controller/GlobalSearchValueController.js (1)
263-281: Excellent security-focused refactor!The implementation correctly addresses XSS/HTML injection risks by:
- Escaping all output (both matched and non-matched segments)
- Using a split-based approach that preserves match boundaries while ensuring safe output
- Adding robust error handling with try-catch for regex construction
The split-map pattern is the right choice here because alternatives like
String.replace()cannot safely escape all text while preserving match positions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
What's the purpose of this PR
fix potential issue when highlighting keyword
Follow this checklist to help us incorporate your contribution quickly and easily:
mvn clean testto make sure this pull request doesn't break anything.mvn spotless:applyto format your code.CHANGESlog.Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.