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.LinkedList;
23 import java.util.List;
25 import org.jetbrains.annotations.NotNull;
26 import org.sonar.api.rules.RuleType;
27 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
28 import org.sonar.ce.task.projectanalysis.component.Component;
29 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
30 import org.sonar.ce.task.projectanalysis.issue.IssueVisitor;
31 import org.sonar.core.issue.DefaultIssue;
32 import org.sonar.db.protobuf.DbCommons;
33 import org.sonar.db.protobuf.DbIssues;
35 import static java.util.Objects.requireNonNull;
36 import static java.util.Objects.requireNonNullElse;
38 public class TaintVulnerabilityVisitor extends IssueVisitor {
39 private static final Set<String> TAINT_REPOSITORIES = Set.of("roslyn.sonaranalyzer.security.cs", "javasecurity", "jssecurity", "tssecurity", "phpsecurity", "pythonsecurity");
41 private final PushEventRepository pushEventRepository;
42 private final AnalysisMetadataHolder analysisMetadataHolder;
43 private final TreeRootHolder treeRootHolder;
45 public TaintVulnerabilityVisitor(PushEventRepository pushEventRepository,
46 AnalysisMetadataHolder analysisMetadataHolder, TreeRootHolder treeRootHolder) {
47 this.pushEventRepository = pushEventRepository;
48 this.analysisMetadataHolder = analysisMetadataHolder;
49 this.treeRootHolder = treeRootHolder;
53 public void onIssue(Component component, DefaultIssue issue) {
54 if ((issue.isNew() || issue.isCopied()) && isTaintVulnerability(issue)) {
55 PushEvent<?> pushEvent = raiseTaintVulnerabilityRaisedEvent(component, issue);
56 pushEventRepository.add(pushEvent);
60 public PushEvent<TaintVulnerabilityRaised> raiseTaintVulnerabilityRaisedEvent(Component component, DefaultIssue issue) {
61 TaintVulnerabilityRaised event = prepareEvent(component, issue);
62 return new PushEvent<TaintVulnerabilityRaised>().setName("TaintVulnerabilityRaised").setData(event);
65 private TaintVulnerabilityRaised prepareEvent(Component component, DefaultIssue issue) {
66 TaintVulnerabilityRaised event = new TaintVulnerabilityRaised();
67 event.setProjectKey(issue.projectKey());
68 event.setCreationDate(issue.creationDate().getTime());
69 event.setKey(issue.key());
70 event.setSeverity(issue.severity());
71 event.setRuleKey(issue.getRuleKey().toString());
72 event.setType(issue.type().name());
74 event.setBranch(analysisMetadataHolder.getBranch().getName());
76 DbIssues.Locations issueLocations = requireNonNull(issue.getLocations());
78 Location mainLocation = new Location();
79 mainLocation.setMessage(issue.getMessage());
81 mainLocation.setFilePath(component.getName());
83 TextRange mainLocationTextRange = getTextRange(issueLocations.getTextRange(), issueLocations.getChecksum());
84 mainLocation.setTextRange(mainLocationTextRange);
85 event.setMainLocation(mainLocation);
87 List<Flow> flows = new LinkedList<>();
88 for (DbIssues.Flow sourceFlow : issueLocations.getFlowList()) {
89 Flow flow = new Flow();
90 List<Location> locations = new LinkedList<>();
91 for (DbIssues.Location sourceLocation : sourceFlow.getLocationList()) {
92 Location location = new Location();
93 var locationComponent = treeRootHolder.getComponentByUuid(sourceLocation.getComponentId());
94 location.setFilePath(requireNonNullElse(locationComponent, component).getName());
95 location.setMessage(sourceLocation.getMsg());
97 TextRange textRange = getTextRange(sourceLocation.getTextRange(), sourceLocation.getChecksum());
98 location.setTextRange(textRange);
100 locations.add(location);
102 flow.setLocations(locations);
105 event.setFlows(flows);
110 private static TextRange getTextRange(DbCommons.TextRange source, String checksum) {
111 TextRange textRange = new TextRange();
112 textRange.setStartLine(source.getStartLine());
113 textRange.setStartLineOffset(source.getStartOffset());
114 textRange.setEndLine(source.getEndLine());
115 textRange.setEndLineOffset(source.getEndOffset());
116 textRange.setHash(checksum);
120 private static boolean isTaintVulnerability(DefaultIssue issue) {
121 return TAINT_REPOSITORIES.contains(issue.getRuleKey().repository())
122 && issue.getLocations() != null
123 && !RuleType.SECURITY_HOTSPOT.equals(issue.type());