3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.ce.task.projectanalysis.pushevent;
22 import java.util.Date;
23 import java.util.List;
24 import org.junit.Before;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.mockito.ArgumentMatcher;
28 import org.sonar.api.rule.RuleKey;
29 import org.sonar.api.rules.RuleType;
30 import org.sonar.api.utils.DateUtils;
31 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
32 import org.sonar.ce.task.projectanalysis.analysis.TestBranch;
33 import org.sonar.ce.task.projectanalysis.component.Component.Type;
34 import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule;
35 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
36 import org.sonar.core.issue.DefaultIssue;
37 import org.sonar.core.issue.FieldDiffs;
38 import org.sonar.db.protobuf.DbCommons;
39 import org.sonar.db.protobuf.DbIssues;
40 import org.sonar.db.pushevent.PushEventDto;
41 import org.sonar.server.issue.TaintChecker;
43 import static org.assertj.core.api.Assertions.assertThat;
44 import static org.mockito.ArgumentMatchers.any;
45 import static org.mockito.Mockito.mock;
46 import static org.mockito.Mockito.when;
48 public class PushEventFactoryTest {
50 private final TaintChecker taintChecker = mock(TaintChecker.class);
52 public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule();
54 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule()
55 .setBranch(new TestBranch("develop"));
57 private final PushEventFactory underTest = new PushEventFactory(treeRootHolder, analysisMetadataHolder, taintChecker);
61 when(taintChecker.getTaintRepositories()).thenReturn(List.of("roslyn.sonaranalyzer.security.cs",
62 "javasecurity", "jssecurity", "tssecurity", "phpsecurity", "pythonsecurity"));
63 when(taintChecker.isTaintVulnerability(any())).thenReturn(true);
68 public void raise_event_to_repository_if_taint_vulnerability_is_new() {
69 DefaultIssue defaultIssue = createDefaultIssue()
72 assertThat(underTest.raiseEventOnIssue(defaultIssue))
74 .hasValueSatisfying(pushEventDto -> {
75 assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityRaised");
76 assertThat(pushEventDto.getPayload()).isNotNull();
82 public void raise_event_to_repository_if_taint_vulnerability_is_reopened() {
83 DefaultIssue defaultIssue = createDefaultIssue()
87 .setCurrentChange(new FieldDiffs().setDiff("status", "CLOSED", "OPEN"));
89 assertThat(underTest.raiseEventOnIssue(defaultIssue))
91 .hasValueSatisfying(pushEventDto -> {
92 assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityRaised");
93 assertThat(pushEventDto.getPayload()).isNotNull();
98 public void skip_event_if_taint_vulnerability_status_change() {
99 DefaultIssue defaultIssue = createDefaultIssue()
103 .setCurrentChange(new FieldDiffs().setDiff("status", "OPEN", "FIXED"));
105 assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty();
109 public void raise_event_to_repository_if_taint_vulnerability_is_copied() {
110 DefaultIssue defaultIssue = createDefaultIssue()
113 assertThat(underTest.raiseEventOnIssue(defaultIssue))
115 .hasValueSatisfying(pushEventDto -> {
116 assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityRaised");
117 assertThat(pushEventDto.getPayload()).isNotNull();
122 public void raise_event_to_repository_if_taint_vulnerability_is_closed() {
123 DefaultIssue defaultIssue = createDefaultIssue()
124 .setComponentUuid("")
127 .setBeingClosed(true);
129 assertThat(underTest.raiseEventOnIssue(defaultIssue))
131 .hasValueSatisfying(pushEventDto -> {
132 assertThat(pushEventDto.getName()).isEqualTo("TaintVulnerabilityClosed");
133 assertThat(pushEventDto.getPayload()).isNotNull();
138 public void skip_issue_if_issue_changed() {
139 DefaultIssue defaultIssue = new DefaultIssue()
140 .setComponentUuid("issue-component-uuid")
144 .setType(RuleType.VULNERABILITY)
145 .setCreationDate(DateUtils.parseDate("2022-01-01"))
146 .setRuleKey(RuleKey.of("javasecurity", "S123"));
148 assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty();
152 public void skip_if_issue_not_from_taint_vulnerability_repository() {
153 DefaultIssue defaultIssue = new DefaultIssue()
154 .setComponentUuid("issue-component-uuid")
156 .setType(RuleType.VULNERABILITY)
157 .setRuleKey(RuleKey.of("weirdrepo", "S123"));
159 when(taintChecker.isTaintVulnerability(any())).thenReturn(false);
161 assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty();
163 defaultIssue = new DefaultIssue()
164 .setComponentUuid("issue-component-uuid")
167 .setBeingClosed(true)
168 .setType(RuleType.VULNERABILITY)
169 .setRuleKey(RuleKey.of("weirdrepo", "S123"));
171 assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty();
175 public void skip_if_issue_is_a_hotspot() {
176 DefaultIssue defaultIssue = new DefaultIssue()
177 .setComponentUuid("issue-component-uuid")
179 .setType(RuleType.SECURITY_HOTSPOT)
180 .setRuleKey(RuleKey.of("javasecurity", "S123"));
182 when(taintChecker.isTaintVulnerability(any())).thenReturn(false);
184 assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty();
188 public void skip_if_issue_does_not_have_locations() {
189 DefaultIssue defaultIssue = new DefaultIssue()
190 .setComponentUuid("issue-component-uuid")
192 .setType(RuleType.VULNERABILITY)
193 .setRuleKey(RuleKey.of("javasecurity", "S123"));
195 when(taintChecker.isTaintVulnerability(any())).thenReturn(false);
197 assertThat(underTest.raiseEventOnIssue(defaultIssue)).isEmpty();
200 private void buildComponentTree() {
201 treeRootHolder.setRoot(ReportComponent.builder(Type.PROJECT, 1)
203 .addChildren(ReportComponent.builder(Type.FILE, 2)
204 .setUuid("issue-component-uuid")
206 .addChildren(ReportComponent.builder(Type.FILE, 3)
207 .setUuid("location-component-uuid")
212 private DefaultIssue createDefaultIssue() {
213 return new DefaultIssue()
214 .setComponentUuid("issue-component-uuid")
215 .setType(RuleType.VULNERABILITY)
216 .setCreationDate(new Date())
217 .setLocations(DbIssues.Locations.newBuilder()
218 .addFlow(DbIssues.Flow.newBuilder()
219 .addLocation(DbIssues.Location.newBuilder()
220 .setChecksum("checksum")
221 .setComponentId("location-component-uuid")
224 .setTextRange(DbCommons.TextRange.newBuilder()
228 .setRuleKey(RuleKey.of("javasecurity", "S123"));
231 private static class PushEventMatcher implements ArgumentMatcher<PushEventDto> {
233 private final PushEventDto left;
235 static PushEventMatcher eq(PushEventDto left) {
236 return new PushEventMatcher(left);
239 private PushEventMatcher(PushEventDto left) {
244 public boolean matches(PushEventDto right) {
245 return left.getName().equals(right.getName());