From 452e52ba38fb5939ceec03c6591163cc20cb95af Mon Sep 17 00:00:00 2001 From: Zipeng WU Date: Wed, 30 Mar 2022 12:40:24 +0200 Subject: [PATCH] Fix SSF-241 --- .../IssueChangesEmailTemplate.java | 10 +++--- .../ChangesOnMyIssuesEmailTemplateTest.java | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java index 14beb1e8189..c8f193e32f0 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java @@ -46,6 +46,7 @@ import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Rule import static java.net.URLEncoder.encode; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.function.Function.identity; +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; import static org.sonar.core.util.stream.MoreCollectors.index; import static org.sonar.server.issue.notification.RuleGroup.ISSUES; import static org.sonar.server.issue.notification.RuleGroup.SECURITY_HOTSPOTS; @@ -85,11 +86,11 @@ public abstract class IssueChangesEmailTemplate implements EmailTemplate { */ protected static void toString(StringBuilder sb, Project project) { Optional branchName = project.getBranchName(); + String value = project.getProjectName(); if (branchName.isPresent()) { - sb.append(project.getProjectName()).append(", ").append(branchName.get()); - } else { - sb.append(project.getProjectName()); + value += ", " + branchName.get(); } + sb.append(escapeHtml(value)); } static String toUrlParams(Project project) { @@ -155,7 +156,8 @@ public abstract class IssueChangesEmailTemplate implements EmailTemplate { Rule rule = rules.next(); Collection issues = issuesByRule.get(rule); - sb.append("
  • ").append("Rule ").append(" ").append(rule.getName()).append(" - "); + String ruleName = escapeHtml(rule.getName()); + sb.append("
  • ").append("Rule ").append(" ").append(ruleName).append(" - "); appendIssueLinks(sb, issuePageHref, issues, rule.getRuleType()); sb.append("
  • "); } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssuesEmailTemplateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssuesEmailTemplateTest.java index 66250436ce2..3b5e20f49c8 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssuesEmailTemplateTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/notification/ChangesOnMyIssuesEmailTemplateTest.java @@ -53,6 +53,7 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; @@ -409,6 +410,38 @@ public class ChangesOnMyIssuesEmailTemplateTest { .noMoreBlock(); } + @Test + public void user_input_content_should_be_html_escape() { + Project project = new Project.Builder("uuid").setProjectName("").setKey("project_key").build(); + String ruleName = ""; + String host = randomAlphabetic(15); + Rule rule = newRule(ruleName, randomRuleTypeHotspotExcluded()); + List changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) + .mapToObj(i -> newChangedIssue("issue_" + i, randomValidStatus(), project, rule)) + .collect(toList()); + UserChange userChange = newUserChange(); + when(emailSettings.getServerBaseURL()).thenReturn(host); + + EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); + + assertThat(emailMessage.getMessage()) + .doesNotContain(project.getProjectName()) + .contains(escapeHtml(project.getProjectName())) + .doesNotContain(ruleName) + .contains(escapeHtml(ruleName)); + + String expectedHref = host + "/project/issues?id=" + project.getKey() + + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C")); + String expectedLinkText = "See all " + changedIssues.size() + " issues"; + HtmlFragmentAssert.assertThat(emailMessage.getMessage()) + .hasParagraph().hasParagraph() // skip header + .hasParagraph(project.getProjectName()) + .hasList("Rule " + ruleName + " - " + expectedLinkText) + .withLink(expectedLinkText, expectedHref) + .hasParagraph().hasParagraph() // skip footer + .noMoreBlock(); + } + @Test public void formats_returns_html_message_for_single_issue_on_master_when_user_change() { Project project = newProject("1"); -- 2.39.5