-
-
Notifications
You must be signed in to change notification settings - Fork 50
Split points by documents #120
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
base: master
Are you sure you want to change the base?
Conversation
|
next i want to add an index of documents at the top, maybe with a count of points for each classification per doc |
|
Can we perhaps make this a setting? I think most users would prefer the way we show the points right now - all in one place |
Yea, should we move it to a separate module for clarity? |
|
IMO it improves the clarity and makes the list easier to read because you get that little bit more context. |
|
BTW I believe the settings button at the bottom broke for whatever reason ;D |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis change adds a Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Settings
participant LocalStorage
participant Popup
participant Service as Service (popup)
User->>Settings: Open extension settings
Settings->>LocalStorage: Read pointListStyle
LocalStorage-->>Settings: Return stored preference
Settings->>Settings: Display current style in dropdown
User->>Settings: Change Summary Style
Settings->>LocalStorage: Save new pointListStyle
LocalStorage-->>Settings: Persisted
User->>Popup: Open popup
Popup->>LocalStorage: Hydrate pointListStyle
LocalStorage-->>Popup: Return preference
alt pointListStyle === "docCategories"
Popup->>Service: Fetch points & documents
Service-->>Popup: ServiceResponse with both
Popup->>Service: populateListDocCategories(points, docs)
Service->>Service: Group points by document
Service->>Service: Render unlinked points section
Service-->>Popup: Rendered DOM
else pointListStyle === "unified"
Popup->>Service: Fetch points
Service-->>Popup: ServiceResponse
Popup->>Service: populateListUnified(points)
Service->>Service: Flat list rendering
Service-->>Popup: Rendered DOM
end
Popup->>User: Display formatted point list
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks✅ Passed checks (3 passed)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
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: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/views/style/popup.css (1)
187-191: Fix the typo in the closing div tag.Line 190 has a typo:
/div>should be</div>.Apply this diff:
<div class=""> <div id="pointList" class="pointList"> <a style="display: none">...</a> - /div> + </div> </div>`src/scripts/views/popup/service.ts (1)
183-210: Critical: XSS vulnerability and syntax error in populateListUnified.This function has two issues:
- Line 190: Syntax error -
/div>should be</div>- Line 192: Using
innerHTMLwithout sanitization is a potential XSS vector, though the template is static in this case.Apply this diff to fix the syntax error:
const temp = ` <div class=""> <div id="pointList" class="pointList"> <a style="display: none">...</a> - /div> + </div> </div>`The innerHTML usage here is safe since
tempcontains only static HTML, but consider documenting this or using a safer approach for consistency.
🧹 Nitpick comments (6)
src/scripts/background/install.ts (1)
16-16: Consider adding type safety for the default value.The string literal
"docCategories"is not type-checked against the union type"docCategories" | "unified"defined insrc/scripts/views/popup/state.ts. A typo here would only be caught at runtime.Consider extracting this as a typed constant or adding a type assertion:
+const DEFAULT_POINT_LIST_STYLE: "docCategories" | "unified" = "docCategories"; + export async function handleExtensionInstalled(): Promise<void> { const donationAllowed = donationReminderAllowed(navigator.userAgent); await setLocal({ themeHeader: true, sentry: false, displayDonationReminder: { active: false, allowedPlattform: donationAllowed, }, - pointListStyle: "docCategories" + pointListStyle: DEFAULT_POINT_LIST_STYLE });src/scripts/views/settings/state.ts (1)
60-62: Add validation when reading pointListStyle from storage.The code uses
String(result['pointListStyle'])which could produce invalid values if storage is corrupted or manually edited.Consider validating the stored value:
if (elements.pointListStyle) { - elements.pointListStyle.value = String(result['pointListStyle']); + const storedValue = result['pointListStyle']; + if (storedValue === "docCategories" || storedValue === "unified") { + elements.pointListStyle.value = storedValue; + } else { + elements.pointListStyle.value = "docCategories"; + } }src/scripts/views/popup/service.ts (4)
11-11: Consider making document_id nullable explicitly.Using
document_id?: numbermeans the property can be missing orundefined, but line 219 and 258 check fornull. Consider usingdocument_id?: number | nullfor clarity.
216-216: Use for...of instead of for...in for arrays.Line 216 uses
for (let i of documents)which iterates over values, but the variable is namedi(suggesting an index). This is confusing.Apply this diff for clarity:
- for (let i of documents) { - const element = i; + for (const element of documents) {
303-316: Simplify createSortedPoints logic.The function has unnecessary
ifchecks since the arrays will always exist (even if empty) after proper typing.-function createSortedPoints(sortedPoints:any,pointsList:HTMLElement) { - if (sortedPoints.blocker) { - createPointList(sortedPoints.blocker, pointsList, false); - } - if (sortedPoints.bad) { - createPointList(sortedPoints.bad, pointsList, false); - } - if (sortedPoints.good) { - createPointList(sortedPoints.good, pointsList, false); - } - if (sortedPoints.neutral) { - createPointList(sortedPoints.neutral, pointsList, true); - } +function createSortedPoints(sortedPoints: FilteredPoints, pointsList: HTMLElement) { + createPointList(sortedPoints.blocker, pointsList, false); + createPointList(sortedPoints.bad, pointsList, false); + createPointList(sortedPoints.good, pointsList, false); + createPointList(sortedPoints.neutral, pointsList, true); }
322-322: Non-null assertion may be unsafe.Line 322 uses
pointsFiltered[i]!which assumes the array access is always valid. While the loop bounds ensure this, combining it with optional chaining forcasesuggests potential undefined values.Consider defensive coding:
- const rawTitle = pointsFiltered[i]!.case?.localized_title ?? pointsFiltered[i]!.title; + const point = pointsFiltered[i]; + if (!point) continue; + const rawTitle = point.case?.localized_title ?? point.title;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/views/settings/icons/FormatListBulleted.svgis excluded by!**/*.svg
📒 Files selected for processing (8)
src/scripts/background/install.ts(1 hunks)src/scripts/views/popup/service.ts(4 hunks)src/scripts/views/popup/state.ts(3 hunks)src/scripts/views/settings/handlers.ts(2 hunks)src/scripts/views/settings/state.ts(3 hunks)src/views/popup.html(2 hunks)src/views/settings/settings.html(1 hunks)src/views/style/popup.css(3 hunks)
🧰 Additional context used
🪛 ast-grep (0.40.0)
src/scripts/views/popup/service.ts
[warning] 192-192: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 192-192: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 233-233: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 252-252: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 269-269: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 329-329: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: point.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 233-233: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 252-252: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 269-269: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 329-329: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: point.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
🔇 Additional comments (11)
src/views/popup.html (2)
102-110: LGTM! Document-based structure aligns with new rendering modes.The changes from
pointListtodocumentListand the addition of adocsWithoutPointssection properly support the new document-categorized display mode.
120-120: Good addition of thedeferattribute.The
deferattribute is appropriate for module scripts and ensures proper loading order.src/views/style/popup.css (3)
4-5: Font path update looks correct.The addition of
../prefix to font URLs suggests proper relative path adjustment for the CSS file location.
273-292: Good refactor from ID to class selectors.Changing from
#pointListto.pointListenables multiple point list containers on the page, which is essential for the document-categorized display mode.
294-312: Document header styles look appropriate.The flex layout and styling for document headers support the new document-categorized view effectively.
src/views/settings/settings.html (1)
78-95: LGTM! Settings UI is well-structured and consistent.The new "Summary Style" control follows the existing pattern and provides clear options for users. The values match the types defined in the state module.
src/scripts/views/settings/handlers.ts (1)
11-11: LGTM! Element retrieval follows established pattern.Consistent with other setting elements in the function.
src/scripts/views/settings/state.ts (2)
16-16: LGTM! Added to storage fetch list.Consistent with other settings.
76-76: LGTM! Element collection updated.Consistent with other form elements.
src/scripts/views/popup/service.ts (2)
63-69: LGTM! Clear branching logic based on display style.The conditional logic properly routes to different rendering functions based on the selected style, with appropriate error logging for unsupported values.
7-12: No changes needed—caseis correctly marked as required.The type definition properly marks
caseas required. Evidence from the codebase shows consistent access tocasewithout optional chaining across lines 289, 292, 295, 298, 325, and 326. Line 322's optional chaining oncase(.case?.localized_title) is an outlier and appears to be unnecessary defensive coding. The property is accessed directly as required everywhere else, confirming the type definition is accurate andcaseis always provided in the API response.
| function populateListDocCategories(allPoints: ServicePoint[], documents: ServiceDocument[]) { | ||
| const documentList = document.getElementById('documentList'); | ||
| // Split points by Document and display them seperatly | ||
| for (let i of documents) { | ||
| const element = i; | ||
|
|
||
| const docPoints = allPoints.filter((point:ServicePoint) => point.document_id === element.id) | ||
| const sortedPoints = filterPoints(docPoints) | ||
|
|
||
| if (sortedPoints.blocker.length + sortedPoints.bad.length + sortedPoints.neutral.length + sortedPoints.good.length > 0) { | ||
| const doc = document.createElement('div'); | ||
| const temp = ` | ||
| <div class=""> | ||
| <div class="documentHeader"> | ||
| <h3 class="documentTitle">${element.name}</h3> | ||
| <a href="${element.url}" target="_blank">Read Original></a> | ||
| </div> | ||
| <div id="pointList_${element.id}" class="pointList"> | ||
| <a style="display: none">...</a> | ||
| </div> | ||
| </div>`; | ||
| doc.innerHTML = temp.trim(); | ||
| documentList!.appendChild(doc.firstChild!); | ||
|
|
||
| const pointsList = document.getElementById(`pointList_${element.id}`)! | ||
|
|
||
| createSortetPoints(sortedPoints,pointsList) | ||
| } else { //documents without points | ||
| const docsWithoutPointsWraper = document.getElementById('docsWithoutPointsWraper') | ||
| const docsWithoutPoints = document.getElementById('docsWithoutPoints') | ||
|
|
||
| if (docsWithoutPoints?.style.display === "none") { | ||
| docsWithoutPoints.style.display = "block" | ||
| } | ||
| const doc = document.createElement('div'); | ||
| const temp = ` | ||
| <div class="documentHeader"> | ||
| <h3 class="documentTitle">${element.name}</h3> | ||
| <a href="${element.url}" target="_blank">Read Original></a> | ||
| </div>`; | ||
| doc.innerHTML = temp.trim(); | ||
| docsWithoutPointsWraper!.appendChild(doc.firstChild!); | ||
| } | ||
| } | ||
| //display points not liked to a document | ||
| const noDocPoints = allPoints.filter((point: ServicePoint) => point.document_id === null) | ||
| if (noDocPoints.length > 0) { | ||
| const doc = document.createElement('div'); | ||
| const temp = ` | ||
| <div class=""> | ||
| <div class="documentHeader"> | ||
| <h3 class="documentTitle">Points not linked to a Document</h3> | ||
| </div> | ||
| `.trim(); | ||
| if (wrapper.firstChild) { | ||
| container.appendChild(wrapper.firstChild as HTMLElement); | ||
| <div id="pointList_unlinkedPoints" class="pointList"> | ||
| <a style="display: none">...</a> | ||
| </div> | ||
| </div>`; | ||
| doc.innerHTML = temp.trim(); | ||
| documentList!.appendChild(doc.firstChild!); | ||
| const sortedPoints = filterPoints(noDocPoints) | ||
| const pointsList = document.getElementById(`pointList_unlinkedPoints`)! | ||
| createSortetPoints(sortedPoints,pointsList) | ||
|
|
||
| } | ||
| } |
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.
Critical: Multiple XSS vulnerabilities in populateListDocCategories.
Lines 227-228 and 250-251 insert unsanitized data from the API (element.name and element.url) directly into HTML via innerHTML. This is a critical XSS vulnerability if the API response is compromised or contains malicious content.
The static analysis correctly flags this as CWE-79. You must sanitize or escape the content:
+function escapeHtml(text: string): string {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+}
+
+function escapeUrl(url: string): string {
+ const a = document.createElement('a');
+ a.href = url;
+ return a.href; // Browser normalizes and validates
+}
function populateListDocCategories(allPoints: ServicePoint[], documents: ServiceDocument[]) {
// ... existing code ...
if (sortedPoints.blocker.length + sortedPoints.bad.length + sortedPoints.neutral.length + sortedPoints.good.length > 0) {
const doc = document.createElement('div');
const temp = `
<div class="">
<div class="documentHeader">
- <h3 class="documentTitle">${element.name}</h3>
- <a href="${element.url}" target="_blank">Read Original></a>
+ <h3 class="documentTitle">${escapeHtml(element.name)}</h3>
+ <a href="${escapeUrl(element.url)}" target="_blank">Read Original></a>
</div>Apply the same escaping to lines 250-251 in the documents-without-points section.
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 ast-grep (0.40.0)
[warning] 233-233: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 252-252: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 269-269: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 233-233: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 252-252: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
[warning] 269-269: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: doc.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
🤖 Prompt for AI Agents
In src/scripts/views/popup/service.ts around lines 213-277, the code injects
unsanitized API values (element.name and element.url) directly into innerHTML
causing XSS; fix by building DOM nodes without innerHTML: create elements (div,
h3, a) with document.createElement, set the title text via textContent (or safe
escaping) and assign the link via element.setAttribute('href', safeUrl) after
validating/normalizing the URL (allow only http/https, or prepend https if
missing), add rel="noopener noreferrer" and keep target="_blank", then append
those nodes; apply the exact same change to the documents-without-points block
(lines ~250-251) so both sections use textContent and safe href assignment
instead of string interpolation into innerHTML.
src/scripts/views/popup/service.ts
Outdated
| return filteredPoints | ||
| } | ||
|
|
||
| function createSortetPoints(sortedPoints:any,pointsList:HTMLElement) { |
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.
Fix typo in function name.
createSortetPoints should be createSortedPoints.
Apply this diff:
-function createSortetPoints(sortedPoints:any,pointsList:HTMLElement) {
+function createSortedPoints(sortedPoints: FilteredPoints, pointsList: HTMLElement) {Update all call sites (lines 239 and 274) as well.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function createSortetPoints(sortedPoints:any,pointsList:HTMLElement) { | |
| function createSortedPoints(sortedPoints: FilteredPoints, pointsList: HTMLElement) { |
🤖 Prompt for AI Agents
In src/scripts/views/popup/service.ts around line 303 rename the mis-typed
function createSortetPoints to createSortedPoints and update its declaration
accordingly; then update all call sites that reference the old name (notably at
lines ~239 and ~274) to call createSortedPoints instead so the symbol name is
consistent across file.
| function createPointList(pointsFiltered: ServicePoint[], pointsList: HTMLElement, last: boolean) { | ||
| let added = 0; | ||
| for (let i = 0; i < pointsFiltered.length; i++) { | ||
| const point = document.createElement('div'); | ||
| const pointTitle = pointsFiltered[i]!.case?.localized_title ?? pointsFiltered[i]!.title; | ||
|
|
||
| let temp = ` | ||
| <div class="point ${pointsFiltered[i]!.case.classification}"> | ||
| <img src="icons/${pointsFiltered[i]!.case.classification}.svg"> | ||
| <p>${pointTitle}</p> | ||
| ${renderCuratorTag(pointsFiltered[i]!.status)} | ||
| </div>`; | ||
| point.innerHTML = temp.trim(); | ||
| pointsList.appendChild(point.firstChild!); | ||
| added++; | ||
| if (i !== pointsFiltered.length - 1) { | ||
| const divider = document.createElement('hr'); | ||
| container.appendChild(divider); | ||
| pointsList.appendChild(divider); | ||
| } | ||
| }); | ||
|
|
||
| if (added > 0 && !isLastGroup) { | ||
| } | ||
| if (added !== 0 && !last) { | ||
| const divider = document.createElement('hr'); | ||
| divider.classList.add('group'); | ||
| container.appendChild(divider); | ||
| pointsList.appendChild(divider); | ||
| } | ||
| } |
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.
Critical: XSS vulnerability in createPointList.
Line 327 directly inserts pointTitle (derived from API data) into HTML via innerHTML on line 330. This is a critical XSS vulnerability.
Even though pointTitle uses a fallback to localized_title or title, both come from the API and must be sanitized:
function createPointList(pointsFiltered: ServicePoint[], pointsList: HTMLElement, last: boolean) {
let added = 0;
for (let i = 0; i < pointsFiltered.length; i++) {
const point = document.createElement('div');
- const pointTitle = pointsFiltered[i]!.case?.localized_title ?? pointsFiltered[i]!.title;
+ const rawTitle = pointsFiltered[i]!.case?.localized_title ?? pointsFiltered[i]!.title;
+ const pointTitle = escapeHtml(rawTitle);
let temp = `
<div class="point ${pointsFiltered[i]!.case.classification}">Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 ast-grep (0.40.0)
[warning] 329-329: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: point.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 329-329: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: point.innerHTML = temp.trim()
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
|
|
||
| const darkmode = Boolean(result['darkmode']); | ||
| const storedCuratorMode = Boolean(result['curatorMode']); | ||
| pointListStyle = result['pointListStyle'] as "docCategories" | "unified" |
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.
Add validation for type-asserted storage value.
The type assertion as "docCategories" | "unified" is unsafe because storage could contain any value. This could lead to runtime errors if the stored value is corrupted.
Apply this diff:
- pointListStyle = result['pointListStyle'] as "docCategories" | "unified"
+ const storedStyle = result['pointListStyle'];
+ pointListStyle = (storedStyle === "docCategories" || storedStyle === "unified")
+ ? storedStyle
+ : "docCategories";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pointListStyle = result['pointListStyle'] as "docCategories" | "unified" | |
| const storedStyle = result['pointListStyle']; | |
| pointListStyle = (storedStyle === "docCategories" || storedStyle === "unified") | |
| ? storedStyle | |
| : "docCategories"; |
🤖 Prompt for AI Agents
In src/scripts/views/popup/state.ts around line 45, the code unsafely
type-asserts a storage value to "docCategories" | "unified"; validate the value
at runtime instead of using a direct type assertion. Read the raw value, check
it against an allowed set (e.g., ["docCategories","unified"]), and if it matches
assign it, otherwise assign a safe default (or remove the key) and optionally
log/debug the unexpected value; ensure the variable ends up typed correctly
without relying on an unchecked "as" cast.
| if (pointListStyleSelect) { | ||
| pointListStyleSelect.addEventListener('change', () => { | ||
| const normalized = pointListStyleSelect.value ?? 'docCategories'; | ||
| if (pointListStyleSelect.value !== normalized) { | ||
| pointListStyleSelect.value = normalized; | ||
| } | ||
| void setLocal({ pointListStyle: normalized }); | ||
| }); | ||
| } |
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.
🛠️ Refactor suggestion | 🟠 Major
Improve type safety and validation for pointListStyle.
The current implementation has two issues:
- The normalization logic (
pointListStyleSelect.value ?? 'docCategories') won't catch invalid values already in the select element, unlike the language handler which usesnormalizeLanguage(). - The
normalizedvariable and the persisted value lack type safety.
Consider adding proper validation:
+function normalizePointListStyle(value: string): "docCategories" | "unified" {
+ if (value === "docCategories" || value === "unified") {
+ return value;
+ }
+ return "docCategories";
+}
+
if (pointListStyleSelect) {
pointListStyleSelect.addEventListener('change', () => {
- const normalized = pointListStyleSelect.value ?? 'docCategories';
+ const normalized = normalizePointListStyle(pointListStyleSelect.value);
if (pointListStyleSelect.value !== normalized) {
pointListStyleSelect.value = normalized;
}
void setLocal({ pointListStyle: normalized });
});
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (pointListStyleSelect) { | |
| pointListStyleSelect.addEventListener('change', () => { | |
| const normalized = pointListStyleSelect.value ?? 'docCategories'; | |
| if (pointListStyleSelect.value !== normalized) { | |
| pointListStyleSelect.value = normalized; | |
| } | |
| void setLocal({ pointListStyle: normalized }); | |
| }); | |
| } | |
| function normalizePointListStyle(value: string): "docCategories" | "unified" { | |
| if (value === "docCategories" || value === "unified") { | |
| return value; | |
| } | |
| return "docCategories"; | |
| } | |
| if (pointListStyleSelect) { | |
| pointListStyleSelect.addEventListener('change', () => { | |
| const normalized = normalizePointListStyle(pointListStyleSelect.value); | |
| if (pointListStyleSelect.value !== normalized) { | |
| pointListStyleSelect.value = normalized; | |
| } | |
| void setLocal({ pointListStyle: normalized }); | |
| }); | |
| } |
🤖 Prompt for AI Agents
In src/scripts/views/settings/handlers.ts around lines 58 to 66, the change
handler uses pointListStyleSelect.value ?? 'docCategories' which neither
validates against allowed values nor preserves type safety; replace this with a
validation/normalization step that checks pointListStyleSelect.value against the
known allowed pointListStyle values (e.g., an enum or const array), map invalid
or missing values to the default 'docCategories', and narrow the type with a
type guard or typed cast before calling setLocal; ensure the persisted value is
of the declared pointListStyle type and update the handler to use that validated
value only.
|
@shadowwwind want me to take over and finish the implementation? |
|





Summary by CodeRabbit
New Features
Style