Skip to content

Commit 07902c0

Browse files
fix: Prevent XSS vulnerability in report rendering (#546)
Apply escapeHtml() to all user-controlled data from GitHub/GitLab API responses before HTML insertion. Fixes XSS vulnerabilities in PR titles, issue titles, commit messages, and repository names. - Escape title, project, html_url in all PR/issue rendering - Escape commit.messageHeadline in commit lists - Escape reviewed PR titles and URLs - Preserve all existing HTML structure and functionality Security Impact: - Prevents arbitrary JavaScript execution from malicious API data - Protects GitHub/GitLab tokens and user activity data - Secures both extension popup and email compose window contexts Fixes #546
1 parent a840fc4 commit 07902c0

1 file changed

Lines changed: 19 additions & 19 deletions

File tree

src/scripts/scrumHelper.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,11 +1464,11 @@ ${blockerText}`;
14641464
let prText = '';
14651465
prText +=
14661466
"<a href='" +
1467-
pr_arr.html_url +
1467+
escapeHtml(pr_arr.html_url) +
14681468
"' target='_blank' rel='noopener noreferrer'>#" +
14691469
pr_arr.number +
14701470
'</a> (' +
1471-
pr_arr.title +
1471+
escapeHtml(pr_arr.title) +
14721472
') ';
14731473
if (showOpenLabel && pr_arr.state === 'open') prText += issue_opened_button;
14741474
// Do not show closed label for reviewed PRs
@@ -1482,11 +1482,11 @@ ${blockerText}`;
14821482
let prText1 = '';
14831483
prText1 +=
14841484
"<li><a href='" +
1485-
pr_arr1.html_url +
1485+
escapeHtml(pr_arr1.html_url) +
14861486
"' target='_blank' rel='noopener noreferrer'>#" +
14871487
pr_arr1.number +
14881488
'</a> (' +
1489-
pr_arr1.title +
1489+
escapeHtml(pr_arr1.title) +
14901490
') ';
14911491
if (showOpenLabel && pr_arr1.state === 'open') prText1 += issue_opened_button;
14921492
// Do not show closed label for reviewed PRs
@@ -1768,31 +1768,31 @@ ${blockerText}`;
17681768
}
17691769

17701770
if (isDraft) {
1771-
li = `<li><i>(${project})</i> - Made PR <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${title}</a>${showOpenLabel ? ' ' + pr_draft_button : ''}`;
1771+
li = `<li><i>(${escapeHtml(project)})</i> - Made PR <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${escapeHtml(title)}</a>${showOpenLabel ? ' ' + pr_draft_button : ''}`;
17721772
if (showCommits && item._allCommits && item._allCommits.length && !isNewPR) {
17731773
log(`[PR DEBUG] Rendering commits for existing draft PR #${number}:`, item._allCommits);
17741774
li += '<ul>';
17751775
item._allCommits.forEach((commit) => {
1776-
li += `<li style=\"list-style: disc; color: #666;\"><span style=\"color:#2563eb;\">${commit.messageHeadline}</span><span style=\"color:#666; font-size: 11px;\"> (${new Date(commit.committedDate).toLocaleString()})</span></li>`;
1776+
li += `<li style=\"list-style: disc; color: #666;\"><span style=\"color:#2563eb;\">${escapeHtml(commit.messageHeadline)}</span><span style=\"color:#666; font-size: 11px;\"> (${new Date(commit.committedDate).toLocaleString()})</span></li>`;
17771777
});
17781778
li += '</ul>';
17791779
}
17801780
li += `</li>`;
17811781
} else if (item.state === 'open' || item.state === 'opened') {
1782-
li = `<li><i>(${project})</i> - ${prAction} <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${title}</a>${showOpenLabel ? ' ' + pr_open_button : ''}`;
1782+
li = `<li><i>(${escapeHtml(project)})</i> - ${prAction} <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${escapeHtml(title)}</a>${showOpenLabel ? ' ' + pr_open_button : ''}`;
17831783

17841784
if (showCommits && item._allCommits && item._allCommits.length && !isNewPR) {
17851785
log(`[PR DEBUG] Rendering commits for existing PR #${number}:`, item._allCommits);
17861786
li += '<ul>';
17871787
item._allCommits.forEach((commit) => {
17881788
li += `<li style="list-style: disc; color: #666;">
1789-
<span style="color:#2563eb;">${commit.messageHeadline}</span><span style="color:#666; font-size: 11px;"> (${new Date(commit.committedDate).toLocaleString()})</span></li>`;
1789+
<span style="color:#2563eb;">${escapeHtml(commit.messageHeadline)}</span><span style="color:#666; font-size: 11px;"> (${new Date(commit.committedDate).toLocaleString()})</span></li>`;
17901790
});
17911791
li += '</ul>';
17921792
}
17931793
li += `</li>`;
17941794
} else if (platform === 'gitlab' && item.state === 'closed') {
1795-
li = `<li><i>(${project})</i> - ${prAction} <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${title}</a>${showOpenLabel ? ' ' + pr_closed_button : ''}</li>`;
1795+
li = `<li><i>(${escapeHtml(project)})</i> - ${prAction} <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${escapeHtml(title)}</a>${showOpenLabel ? ' ' + pr_closed_button : ''}</li>`;
17961796
} else {
17971797
let merged = null;
17981798
if ((githubToken || (useMergedStatus && !fallbackToSimple)) && mergedStatusResults) {
@@ -1802,10 +1802,10 @@ ${blockerText}`;
18021802
merged = mergedStatusResults[`${owner}/${repo}#${number}`];
18031803
}
18041804
if (merged === true) {
1805-
li = `<li><i>(${project})</i> - ${prAction} <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${title}</a>${showOpenLabel ? ' ' + pr_merged_button : ''}</li>`;
1805+
li = `<li><i>(${escapeHtml(project)})</i> - ${prAction} <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${escapeHtml(title)}</a>${showOpenLabel ? ' ' + pr_merged_button : ''}</li>`;
18061806
} else {
18071807
// Always show closed label for merged === false or merged === null/undefined
1808-
li = `<li><i>(${project})</i> - ${prAction} <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${html_url}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${title}</a>${showOpenLabel ? ' ' + pr_closed_button : ''}</li>`;
1808+
li = `<li><i>(${escapeHtml(project)})</i> - ${prAction} <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>(#${number})</a> - <a href='${escapeHtml(html_url)}' target='_blank' rel='noopener noreferrer' contenteditable='false'>${escapeHtml(title)}</a>${showOpenLabel ? ' ' + pr_closed_button : ''}</li>`;
18091809
}
18101810
}
18111811
log('[SCRUM-DEBUG] Added PR/MR to lastWeekArray:', li, item);
@@ -1816,13 +1816,13 @@ ${blockerText}`;
18161816
if (item.state === 'open' && item.body?.toUpperCase().indexOf('YES') > 0) {
18171817
const li2 =
18181818
'<li><i>(' +
1819-
project +
1819+
escapeHtml(project) +
18201820
')</i> - Work on Issue(#' +
18211821
number +
18221822
") - <a href='" +
1823-
html_url +
1823+
escapeHtml(html_url) +
18241824
"' target='_blank' rel='noopener noreferrer'>" +
1825-
title +
1825+
escapeHtml(title) +
18261826
'</a>' +
18271827
(showOpenLabel ? ' ' + issue_opened_button : '') +
18281828
'&nbsp;&nbsp;</li>';
@@ -1836,19 +1836,19 @@ ${blockerText}`;
18361836
const isCreatedToday = today.getTime() === itemCreatedDate.getTime();
18371837
const issueActionText = isCreatedToday ? 'Opened Issue' : 'Updated Issue';
18381838
if (item.state === 'open') {
1839-
li = `<li><i>(${project})</i> - ${issueActionText}(#${number}) - <a href='${html_url}'>${title}</a>${showOpenLabel ? ' ' + issue_opened_button : ''}</li>`;
1839+
li = `<li><i>(${escapeHtml(project)})</i> - ${issueActionText}(#${number}) - <a href='${escapeHtml(html_url)}'>${escapeHtml(title)}</a>${showOpenLabel ? ' ' + issue_opened_button : ''}</li>`;
18401840
} else if (item.state === 'closed') {
18411841
// Use state_reason to distinguish closure reason
18421842
if (item.state_reason === 'completed') {
1843-
li = `<li><i>(${project})</i> - ${issueActionText}(#${number}) - <a href='${html_url}'>${title}</a> ${issue_closed_completed_button}</li>`;
1843+
li = `<li><i>(${escapeHtml(project)})</i> - ${issueActionText}(#${number}) - <a href='${escapeHtml(html_url)}'>${escapeHtml(title)}</a> ${issue_closed_completed_button}</li>`;
18441844
} else if (item.state_reason === 'not_planned') {
1845-
li = `<li><i>(${project})</i> - ${issueActionText}(#${number}) - <a href='${html_url}'>${title}</a> ${issue_closed_notplanned_button}</li>`;
1845+
li = `<li><i>(${escapeHtml(project)})</i> - ${issueActionText}(#${number}) - <a href='${escapeHtml(html_url)}'>${escapeHtml(title)}</a> ${issue_closed_notplanned_button}</li>`;
18461846
} else {
1847-
li = `<li><i>(${project})</i> - ${issueActionText}(#${number}) - <a href='${html_url}'>${title}</a> ${issue_closed_button}</li>`;
1847+
li = `<li><i>(${escapeHtml(project)})</i> - ${issueActionText}(#${number}) - <a href='${escapeHtml(html_url)}'>${escapeHtml(title)}</a> ${issue_closed_button}</li>`;
18481848
}
18491849
} else {
18501850
// Fallback for unexpected state
1851-
li = `<li><i>(${project})</i> - ${issueActionText}(#${number}) - <a href='${html_url}'>${title}</a></li>`;
1851+
li = `<li><i>(${escapeHtml(project)})</i> - ${issueActionText}(#${number}) - <a href='${escapeHtml(html_url)}'>${escapeHtml(title)}</a></li>`;
18521852
}
18531853

18541854
log('[SCRUM-DEBUG] Added issue to lastWeekArray:', li, item);

0 commit comments

Comments
 (0)