Selaa lähdekoodia

SONAR-16386 - Persist TaintVulnerabilityClosed in DB

tags/9.6.0.59041
Belen Pruvost 1 vuosi sitten
vanhempi
commit
cc0269b8ba

+ 2
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java Näytä tiedosto

@@ -279,12 +279,13 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
IssuesRepositoryVisitor.class,
RemoveProcessedComponentsVisitor.class,
IssueOnReferenceBranchVisitor.class,
TaintVulnerabilityVisitor.class,

// visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues
AnalysisFromSonarQube94Visitor.class,
LoadComponentUuidsHavingOpenIssuesVisitor.class,
IntegrateIssuesVisitor.class,
TaintChecker.class,
TaintVulnerabilityVisitor.class,
CloseIssuesOnRemovedComponentsVisitor.class,
MaintainabilityMeasuresVisitor.class,
NewMaintainabilityMeasuresVisitor.class,

+ 20
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitor.java Näytä tiedosto

@@ -24,8 +24,12 @@ import java.util.Set;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.ce.task.projectanalysis.pushevent.PushEvent;
import org.sonar.ce.task.projectanalysis.pushevent.PushEventRepository;
import org.sonar.ce.task.projectanalysis.pushevent.TaintVulnerabilityClosed;
import org.sonar.ce.task.projectanalysis.util.cache.DiskCache.CacheAppender;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.server.issue.TaintChecker;

import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;

@@ -38,14 +42,19 @@ public class CloseIssuesOnRemovedComponentsVisitor extends TypeAwareVisitorAdapt
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues;
private final ProtoIssueCache protoIssueCache;
private final IssueLifecycle issueLifecycle;
private final PushEventRepository pushEventRepository;
private final TaintChecker taintChecker;


public CloseIssuesOnRemovedComponentsVisitor(ComponentIssuesLoader issuesLoader, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, ProtoIssueCache protoIssueCache,
IssueLifecycle issueLifecycle) {
IssueLifecycle issueLifecycle, PushEventRepository pushEventRepository, TaintChecker taintChecker) {
super(CrawlerDepthLimit.PROJECT, POST_ORDER);
this.issuesLoader = issuesLoader;
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues;
this.protoIssueCache = protoIssueCache;
this.issueLifecycle = issueLifecycle;
this.pushEventRepository = pushEventRepository;
this.taintChecker = taintChecker;
}

@Override
@@ -63,8 +72,18 @@ public class CloseIssuesOnRemovedComponentsVisitor extends TypeAwareVisitorAdapt
issue.setOnDisabledRule(false);
issueLifecycle.doAutomaticTransition(issue);
cacheAppender.append(issue);
addPushEventIfTaintVulnerability(issue);
}
}
}
}

private void addPushEventIfTaintVulnerability(DefaultIssue issue) {
if (taintChecker.isTaintVulnerability(issue)) {
TaintVulnerabilityClosed event = new TaintVulnerabilityClosed(issue.key(), issue.projectKey());
PushEvent<?> pushEvent = new PushEvent<TaintVulnerabilityClosed>().setName("TaintVulnerabilityClosed").setData(event);
pushEventRepository.add(pushEvent);
}
}

}

+ 52
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityClosed.java Näytä tiedosto

@@ -0,0 +1,52 @@
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.pushevent;

public class TaintVulnerabilityClosed {

private String key;
private String projectKey;

public TaintVulnerabilityClosed() {
// nothing to do
}

public TaintVulnerabilityClosed(String key, String projectKey) {
this.key = key;
this.projectKey = projectKey;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getProjectKey() {
return projectKey;
}

public void setProjectKey(String projectKey) {
this.projectKey = projectKey;
}

}

+ 16
- 9
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitor.java Näytä tiedosto

@@ -22,7 +22,6 @@ package org.sonar.ce.task.projectanalysis.pushevent;
import java.util.LinkedList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.sonar.api.rules.RuleType;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
@@ -53,13 +52,22 @@ public class TaintVulnerabilityVisitor extends IssueVisitor {

@Override
public void onIssue(Component component, DefaultIssue issue) {
if ((issue.isNew() || issue.isCopied()) && isTaintVulnerability(issue)) {
if (!taintChecker.isTaintVulnerability(issue)) {
return;
}
if (issue.isNew() || issue.isCopied()) {
PushEvent<?> pushEvent = raiseTaintVulnerabilityRaisedEvent(component, issue);
pushEventRepository.add(pushEvent);
return;
}

if (issue.isBeingClosed()) {
PushEvent<?> pushEvent = raiseTaintVulnerabilityClosedEvent(issue);
pushEventRepository.add(pushEvent);
}
}

public PushEvent<TaintVulnerabilityRaised> raiseTaintVulnerabilityRaisedEvent(Component component, DefaultIssue issue) {
private PushEvent<TaintVulnerabilityRaised> raiseTaintVulnerabilityRaisedEvent(Component component, DefaultIssue issue) {
TaintVulnerabilityRaised event = prepareEvent(component, issue);
return new PushEvent<TaintVulnerabilityRaised>().setName("TaintVulnerabilityRaised").setData(event);
}
@@ -108,6 +116,11 @@ public class TaintVulnerabilityVisitor extends IssueVisitor {
return event;
}

private static PushEvent<TaintVulnerabilityClosed> raiseTaintVulnerabilityClosedEvent(DefaultIssue issue) {
TaintVulnerabilityClosed event = new TaintVulnerabilityClosed(issue.key(), issue.projectKey());
return new PushEvent<TaintVulnerabilityClosed>().setName("TaintVulnerabilityClosed").setData(event);
}

@NotNull
private static TextRange getTextRange(DbCommons.TextRange source, String checksum) {
TextRange textRange = new TextRange();
@@ -118,10 +131,4 @@ public class TaintVulnerabilityVisitor extends IssueVisitor {
textRange.setHash(checksum);
return textRange;
}

private boolean isTaintVulnerability(DefaultIssue issue) {
return taintChecker.getTaintRepositories().contains(issue.getRuleKey().repository())
&& issue.getLocations() != null
&& !RuleType.SECURITY_HOTSPOT.equals(issue.type());
}
}

+ 41
- 1
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java Näytä tiedosto

@@ -26,16 +26,22 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.System2;
import org.sonar.ce.task.projectanalysis.component.ReportComponent;
import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
import org.sonar.ce.task.projectanalysis.pushevent.PushEvent;
import org.sonar.ce.task.projectanalysis.pushevent.PushEventRepository;
import org.sonar.ce.task.projectanalysis.pushevent.TaintVulnerabilityClosed;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.util.CloseableIterator;
import org.sonar.server.issue.TaintChecker;

import static com.google.common.collect.Sets.newHashSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@@ -54,12 +60,15 @@ public class CloseIssuesOnRemovedComponentsVisitorTest {
IssueLifecycle issueLifecycle = mock(IssueLifecycle.class);
ProtoIssueCache protoIssueCache;
VisitorsCrawler underTest;
PushEventRepository pushEventRepository = mock(PushEventRepository.class);
TaintChecker taintChecker = mock(TaintChecker.class);

@Before
public void setUp() throws Exception {
protoIssueCache = new ProtoIssueCache(temp.newFile(), System2.INSTANCE);
underTest = new VisitorsCrawler(
Arrays.asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues, protoIssueCache, issueLifecycle)));
Arrays.asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues,
protoIssueCache, issueLifecycle, pushEventRepository, taintChecker)));
}

@Test
@@ -71,10 +80,41 @@ public class CloseIssuesOnRemovedComponentsVisitorTest {
DefaultIssue issue = new DefaultIssue().setKey(issueUuid).setType(RuleType.BUG).setCreationDate(new Date())
.setComponentKey("c").setProjectUuid("u").setProjectKey("k").setRuleKey(RuleKey.of("r", "r")).setStatus("OPEN");
when(issuesLoader.loadOpenIssues(fileUuid)).thenReturn(Collections.singletonList(issue));
when(taintChecker.isTaintVulnerability(any())).thenReturn(false);

underTest.visit(ReportComponent.builder(PROJECT, 1).build());

verify(issueLifecycle).doAutomaticTransition(issue);
verifyNoInteractions(pushEventRepository);
CloseableIterator<DefaultIssue> issues = protoIssueCache.traverse();
assertThat(issues.hasNext()).isTrue();

DefaultIssue result = issues.next();
assertThat(result.key()).isEqualTo(issueUuid);
assertThat(result.isBeingClosed()).isTrue();
assertThat(result.isOnDisabledRule()).isFalse();
}

@Test
public void close_taint_vulnerability() {
String fileUuid = "FILE1";
String issueUuid = "ABCD";

when(componentsWithUnprocessedIssues.getUuids()).thenReturn(newHashSet(fileUuid));
DefaultIssue issue = new DefaultIssue().setKey(issueUuid).setType(RuleType.BUG).setCreationDate(new Date())
.setComponentKey("c").setProjectUuid("u").setProjectKey("k").setRuleKey(RuleKey.of("r", "r")).setStatus("OPEN");
when(issuesLoader.loadOpenIssues(fileUuid)).thenReturn(Collections.singletonList(issue));
when(taintChecker.isTaintVulnerability(any())).thenReturn(true);

underTest.visit(ReportComponent.builder(PROJECT, 1).build());

verify(issueLifecycle).doAutomaticTransition(issue);

ArgumentCaptor<PushEvent<TaintVulnerabilityClosed>> pushEventCaptor = ArgumentCaptor.forClass(PushEvent.class);
verify(pushEventRepository).add(pushEventCaptor.capture());
PushEvent<TaintVulnerabilityClosed> pushEvent = pushEventCaptor.getValue();
assertThat(pushEvent.getName()).isEqualTo("TaintVulnerabilityClosed");

CloseableIterator<DefaultIssue> issues = protoIssueCache.traverse();
assertThat(issues.hasNext()).isTrue();


+ 45
- 0
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityClosedTest.java Näytä tiedosto

@@ -0,0 +1,45 @@
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.projectanalysis.pushevent;

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;

public class TaintVulnerabilityClosedTest {

@Test
public void testConstructor_Getters_Setters() {
TaintVulnerabilityClosed underTest = new TaintVulnerabilityClosed();
assertThat(underTest.getKey()).isNull();
assertThat(underTest.getProjectKey()).isNull();

underTest = new TaintVulnerabilityClosed("issue-key", "project-key");
assertThat(underTest.getKey()).isEqualTo("issue-key");
assertThat(underTest.getProjectKey()).isEqualTo("project-key");

underTest.setKey("another-issue-key");
assertThat(underTest.getKey()).isEqualTo("another-issue-key");
underTest.setProjectKey("another-project-key");
assertThat(underTest.getProjectKey()).isEqualTo("another-project-key");

}
}

+ 36
- 0
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitorTest.java Näytä tiedosto

@@ -27,6 +27,7 @@ import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.DateUtils;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.analysis.TestBranch;
import org.sonar.ce.task.projectanalysis.component.Component;
@@ -65,6 +66,7 @@ public class TaintVulnerabilityVisitorTest {
public void setUp() {
when(taintChecker.getTaintRepositories()).thenReturn(List.of("roslyn.sonaranalyzer.security.cs",
"javasecurity", "jssecurity", "tssecurity", "phpsecurity", "pythonsecurity"));
when(taintChecker.isTaintVulnerability(any())).thenReturn(true);
}

@Test
@@ -91,13 +93,30 @@ public class TaintVulnerabilityVisitorTest {
verify(repositoryMock).add(argThat(PushEventMatcher.eq(new PushEvent<>().setName("TaintVulnerabilityRaised"))));
}

@Test
public void add_event_to_repository_if_taint_vulnerability_is_closed() {
buildComponentTree();
Component component = createIssueComponent();
DefaultIssue defaultIssue = createDefaultIssue()
.setNew(false)
.setCopied(false)
.setBeingClosed(true);

underTest.onIssue(component, defaultIssue);

verify(repositoryMock).add(argThat(PushEventMatcher.eq(new PushEvent<>().setName("TaintVulnerabilityClosed"))));
}

@Test
public void skip_issue_if_issue_changed() {
Component component = createIssueComponent();

DefaultIssue defaultIssue = new DefaultIssue()
.setNew(false)
.setCopied(false)
.setChanged(true)
.setType(RuleType.VULNERABILITY)
.setCreationDate(DateUtils.parseDate("2022-01-01"))
.setRuleKey(RuleKey.of("javasecurity", "S123"));

underTest.onIssue(component, defaultIssue);
@@ -114,6 +133,19 @@ public class TaintVulnerabilityVisitorTest {
.setType(RuleType.VULNERABILITY)
.setRuleKey(RuleKey.of("weirdrepo", "S123"));

when(taintChecker.isTaintVulnerability(any())).thenReturn(false);

underTest.onIssue(component, defaultIssue);

verify(repositoryMock, times(0)).add(any(PushEvent.class));

defaultIssue = new DefaultIssue()
.setChanged(false)
.setNew(false)
.setBeingClosed(true)
.setType(RuleType.VULNERABILITY)
.setRuleKey(RuleKey.of("weirdrepo", "S123"));

underTest.onIssue(component, defaultIssue);

verify(repositoryMock, times(0)).add(any(PushEvent.class));
@@ -128,6 +160,8 @@ public class TaintVulnerabilityVisitorTest {
.setType(RuleType.SECURITY_HOTSPOT)
.setRuleKey(RuleKey.of("javasecurity", "S123"));

when(taintChecker.isTaintVulnerability(any())).thenReturn(false);

underTest.onIssue(component, defaultIssue);

verify(repositoryMock, times(0)).add(any(PushEvent.class));
@@ -142,6 +176,8 @@ public class TaintVulnerabilityVisitorTest {
.setType(RuleType.VULNERABILITY)
.setRuleKey(RuleKey.of("javasecurity", "S123"));

when(taintChecker.isTaintVulnerability(any())).thenReturn(false);

underTest.onIssue(component, defaultIssue);

verify(repositoryMock, times(0)).add(any(PushEvent.class));

+ 9
- 0
server/sonar-server-common/src/main/java/org/sonar/server/issue/TaintChecker.java Näytä tiedosto

@@ -28,6 +28,8 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.sonar.api.config.Configuration;
import org.sonar.api.rules.RuleType;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.issue.IssueDto;

public class TaintChecker {
@@ -88,4 +90,11 @@ public class TaintChecker {
return repositories;
}


public boolean isTaintVulnerability(DefaultIssue issue) {
return taintRepositories.contains(issue.getRuleKey().repository())
&& issue.getLocations() != null
&& !RuleType.SECURITY_HOTSPOT.equals(issue.type());
}

}

+ 35
- 0
server/sonar-server-common/src/test/java/org/sonar/server/issue/TaintCheckerTest.java Näytä tiedosto

@@ -22,9 +22,14 @@ package org.sonar.server.issue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.junit.Test;
import org.sonar.api.config.Configuration;
import org.sonar.api.rules.RuleType;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.protobuf.DbCommons;
import org.sonar.db.protobuf.DbIssues;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -98,6 +103,29 @@ public class TaintCheckerTest {
"tssecurity", "phpsecurity", "pythonsecurity", "extra-1", "extra-2");
}

@Test
public void test_isTaintVulnerability() {
DefaultIssue taintWithoutLocation = createIssueWithRepository("noTaintIssue", "roslyn.sonaranalyzer.security.cs")
.toDefaultIssue();
DefaultIssue taint = createIssueWithRepository("taintIssue", "roslyn.sonaranalyzer.security.cs")
.setLocations(DbIssues.Locations.newBuilder()
.setTextRange(DbCommons.TextRange.newBuilder().build())
.build())
.toDefaultIssue();
DefaultIssue issue = createIssueWithRepository("standardIssue", "java")
.setLocations(DbIssues.Locations.newBuilder()
.setTextRange(DbCommons.TextRange.newBuilder().build())
.build())
.toDefaultIssue();
DefaultIssue hotspot = createIssueWithRepository("hotspot", "roslyn.sonaranalyzer.security.cs",
RuleType.SECURITY_HOTSPOT).toDefaultIssue();

assertThat(underTest.isTaintVulnerability(taintWithoutLocation)).isFalse();
assertThat(underTest.isTaintVulnerability(taint)).isTrue();
assertThat(underTest.isTaintVulnerability(issue)).isFalse();
assertThat(underTest.isTaintVulnerability(hotspot)).isFalse();
}

private List<IssueDto> getIssues() {
List<IssueDto> issues = new ArrayList<>();

@@ -116,9 +144,16 @@ public class TaintCheckerTest {
}

private IssueDto createIssueWithRepository(String issueKey, String repository) {
return createIssueWithRepository(issueKey, repository, null);
}


private IssueDto createIssueWithRepository(String issueKey, String repository, @Nullable RuleType ruleType) {
IssueDto issueDto = new IssueDto();
issueDto.setStatus("OPEN");
issueDto.setKee(issueKey);
issueDto.setRuleKey(repository, "S1");
issueDto.setType(ruleType == null ? RuleType.VULNERABILITY : ruleType);
return issueDto;
}


Loading…
Peruuta
Tallenna