diff options
author | Jacek <jacek.poreda@sonarsource.com> | 2022-07-11 18:24:56 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-07-25 20:03:57 +0000 |
commit | d66a7c69cbef6ee9647ac0233d915b2863c06338 (patch) | |
tree | 4888d2b6bb5dfc7335ff235bcbbdf0b33c14d05b /server/sonar-ce-task-projectanalysis | |
parent | 31184bdc5b3c24d88f424c1625bedc36513884cc (diff) | |
download | sonarqube-d66a7c69cbef6ee9647ac0233d915b2863c06338.tar.gz sonarqube-d66a7c69cbef6ee9647ac0233d915b2863c06338.zip |
SONAR-16374 Add persist step in CE to push events to DB
Diffstat (limited to 'server/sonar-ce-task-projectanalysis')
20 files changed, 1125 insertions, 63 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolder.java index c4fbdc4e86f..324645cdc89 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolder.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolder.java @@ -61,6 +61,13 @@ public interface TreeRootHolder { */ Component getComponentByRef(int ref); + /** + * Return a component by its uuid + * + * @throws IllegalStateException if the holder is empty (ie. there is no root yet) + * @throws IllegalArgumentException if there's no {@link Component} with the specified reference + */ + Component getComponentByUuid(String uuid); /** * Return a component by its batch reference. Returns {@link Optional#empty()} if there's diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderImpl.java index 716f0b2c437..abb4dc2ed91 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderImpl.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderImpl.java @@ -20,12 +20,14 @@ package org.sonar.ce.task.projectanalysis.component; import com.google.common.collect.ImmutableMap; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import javax.annotation.CheckForNull; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.commons.lang.StringUtils.isNotBlank; import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER; /** @@ -36,6 +38,8 @@ public class TreeRootHolderImpl implements MutableTreeRootHolder { private Map<Integer, Component> componentsByRef = null; @CheckForNull private Map<Integer, Component> extendedComponentsByRef = null; + @CheckForNull + private Map<String, Component> componentsByUuid = null; private int size = 0; private Component root = null; private Component extendedTreeRoot = null; @@ -72,9 +76,16 @@ public class TreeRootHolderImpl implements MutableTreeRootHolder { } @Override + public Component getComponentByUuid(String uuid) { + checkInitialized(); + ensureComponentByRefAndUuidArePopulated(); + return componentsByUuid.get(uuid); + } + + @Override public Optional<Component> getOptionalComponentByRef(int ref) { checkInitialized(); - ensureComponentByRefIsPopulated(); + ensureComponentByRefAndUuidArePopulated(); return Optional.ofNullable(componentsByRef.get(ref)); } @@ -92,7 +103,7 @@ public class TreeRootHolderImpl implements MutableTreeRootHolder { @Override public int getSize() { checkInitialized(); - ensureComponentByRefIsPopulated(); + ensureComponentByRefAndUuidArePopulated(); return size; } @@ -114,23 +125,28 @@ public class TreeRootHolderImpl implements MutableTreeRootHolder { this.extendedComponentsByRef = builder.build(); } - private void ensureComponentByRefIsPopulated() { - if (componentsByRef != null) { + private void ensureComponentByRefAndUuidArePopulated() { + if (componentsByRef != null && componentsByUuid != null) { return; } - final ImmutableMap.Builder<Integer, Component> builder = ImmutableMap.builder(); + final ImmutableMap.Builder<Integer, Component> builderByRef = ImmutableMap.builder(); + final Map<String, Component> builderByUuid = new HashMap<>(); new DepthTraversalTypeAwareCrawler( new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, POST_ORDER) { @Override public void visitAny(Component component) { size++; if (component.getReportAttributes().getRef() != null) { - builder.put(component.getReportAttributes().getRef(), component); + builderByRef.put(component.getReportAttributes().getRef(), component); + } + if (isNotBlank(component.getUuid())) { + builderByUuid.put(component.getUuid(), component); } } }).visit(this.root); - this.componentsByRef = builder.build(); + this.componentsByRef = builderByRef.build(); + this.componentsByUuid = ImmutableMap.copyOf(builderByUuid); } private void checkInitialized() { diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index 89f249a57e2..42489f15c13 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -113,6 +113,8 @@ import org.sonar.ce.task.projectanalysis.notification.NotificationFactory; import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver; import org.sonar.ce.task.projectanalysis.period.NewCodeReferenceBranchComponentUuids; import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl; +import org.sonar.ce.task.projectanalysis.pushevent.PushEventRepositoryImpl; +import org.sonar.ce.task.projectanalysis.pushevent.TaintVulnerabilityVisitor; import org.sonar.ce.task.projectanalysis.qualitygate.EvaluationResultTextConverterImpl; import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateHolderImpl; import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateServiceImpl; @@ -220,6 +222,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop LanguageRepositoryImpl.class, MeasureRepositoryImpl.class, EventRepositoryImpl.class, + PushEventRepositoryImpl.class, ConfigurationRepositoryImpl.class, DisabledComponentsHolderImpl.class, QualityGateServiceImpl.class, @@ -275,6 +278,7 @@ 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, diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java new file mode 100644 index 00000000000..75671df34fe --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java @@ -0,0 +1,38 @@ +/* + * 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 java.util.List; + +public class Flow { + private List<Location> locations; + + public Flow() { + // nothing to do + } + + public List<Location> getLocations() { + return locations; + } + + public void setLocations(List<Location> locations) { + this.locations = locations; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java new file mode 100644 index 00000000000..016dbfa71c9 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java @@ -0,0 +1,54 @@ +/* + * 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 Location { + private String filePath; + private String message; + private TextRange textRange; + + public Location() { + // nothing to do + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public TextRange getTextRange() { + return textRange; + } + + public void setTextRange(TextRange textRange) { + this.textRange = textRange; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEvent.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEvent.java new file mode 100644 index 00000000000..c67520ad67d --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEvent.java @@ -0,0 +1,48 @@ +/* + * 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 PushEvent<E> { + private String name; + private E data; + + public PushEvent() { + // nothing to do + } + + public String getName() { + return name; + } + + public PushEvent<E> setName(String name) { + this.name = name; + return this; + } + + public E getData() { + return data; + } + + public PushEvent<E> setData(E data) { + this.data = data; + return this; + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepository.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepository.java new file mode 100644 index 00000000000..f7a12c2b58c --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepository.java @@ -0,0 +1,30 @@ +/* + * 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 java.util.Collection; + +public interface PushEventRepository { + + void add(PushEvent<?> event); + + Collection<PushEvent<?>> getEvents(); + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImpl.java new file mode 100644 index 00000000000..2aaad3e42f7 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImpl.java @@ -0,0 +1,41 @@ +/* + * 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 java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +public class PushEventRepositoryImpl implements PushEventRepository { + + private final List<PushEvent<?>> events = new LinkedList<>(); + + @Override + public void add(PushEvent<?> event) { + Objects.requireNonNull(event); + events.add(event); + } + + @Override + public Collection<PushEvent<?>> getEvents() { + return new LinkedList<>(events); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java new file mode 100644 index 00000000000..e9ab37d368f --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java @@ -0,0 +1,113 @@ +/* + * 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 java.util.List; + +public class TaintVulnerabilityRaised { + + private String key; + private String projectKey; + private String branch; + private long creationDate; + private String ruleKey; + private String severity; + private String type; + + private Location mainLocation; + + private List<Flow> flows; + + public TaintVulnerabilityRaised() { + // nothing to do + } + + 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; + } + + public String getBranch() { + return branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } + + public long getCreationDate() { + return creationDate; + } + + public void setCreationDate(long creationDate) { + this.creationDate = creationDate; + } + + public String getRuleKey() { + return ruleKey; + } + + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } + + public String getSeverity() { + return severity; + } + + public void setSeverity(String severity) { + this.severity = severity; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Location getMainLocation() { + return mainLocation; + } + + public void setMainLocation(Location mainLocation) { + this.mainLocation = mainLocation; + } + + public List<Flow> getFlows() { + return flows; + } + + public void setFlows(List<Flow> flows) { + this.flows = flows; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitor.java new file mode 100644 index 00000000000..e6531e9164e --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitor.java @@ -0,0 +1,125 @@ +/* + * 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 java.util.LinkedList; +import java.util.List; +import java.util.Set; +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; +import org.sonar.ce.task.projectanalysis.issue.IssueVisitor; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.db.protobuf.DbCommons; +import org.sonar.db.protobuf.DbIssues; + +import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElse; + +public class TaintVulnerabilityVisitor extends IssueVisitor { + private static final Set<String> TAINT_REPOSITORIES = Set.of("roslyn.sonaranalyzer.security.cs", "javasecurity", "jssecurity", "tssecurity", "phpsecurity", "pythonsecurity"); + + private final PushEventRepository pushEventRepository; + private final AnalysisMetadataHolder analysisMetadataHolder; + private final TreeRootHolder treeRootHolder; + + public TaintVulnerabilityVisitor(PushEventRepository pushEventRepository, + AnalysisMetadataHolder analysisMetadataHolder, TreeRootHolder treeRootHolder) { + this.pushEventRepository = pushEventRepository; + this.analysisMetadataHolder = analysisMetadataHolder; + this.treeRootHolder = treeRootHolder; + } + + @Override + public void onIssue(Component component, DefaultIssue issue) { + if ((issue.isNew() || issue.isCopied()) && isTaintVulnerability(issue)) { + PushEvent<?> pushEvent = raiseTaintVulnerabilityRaisedEvent(component, issue); + pushEventRepository.add(pushEvent); + } + } + + public PushEvent<TaintVulnerabilityRaised> raiseTaintVulnerabilityRaisedEvent(Component component, DefaultIssue issue) { + TaintVulnerabilityRaised event = prepareEvent(component, issue); + return new PushEvent<TaintVulnerabilityRaised>().setName("TaintVulnerabilityRaised").setData(event); + } + + private TaintVulnerabilityRaised prepareEvent(Component component, DefaultIssue issue) { + TaintVulnerabilityRaised event = new TaintVulnerabilityRaised(); + event.setProjectKey(issue.projectKey()); + event.setCreationDate(issue.creationDate().getTime()); + event.setKey(issue.key()); + event.setSeverity(issue.severity()); + event.setRuleKey(issue.getRuleKey().toString()); + event.setType(issue.type().name()); + + event.setBranch(analysisMetadataHolder.getBranch().getName()); + + DbIssues.Locations issueLocations = requireNonNull(issue.getLocations()); + + Location mainLocation = new Location(); + mainLocation.setMessage(issue.getMessage()); + + mainLocation.setFilePath(component.getName()); + + TextRange mainLocationTextRange = getTextRange(issueLocations.getTextRange(), issueLocations.getChecksum()); + mainLocation.setTextRange(mainLocationTextRange); + event.setMainLocation(mainLocation); + + List<Flow> flows = new LinkedList<>(); + for (DbIssues.Flow sourceFlow : issueLocations.getFlowList()) { + Flow flow = new Flow(); + List<Location> locations = new LinkedList<>(); + for (DbIssues.Location sourceLocation : sourceFlow.getLocationList()) { + Location location = new Location(); + var locationComponent = treeRootHolder.getComponentByUuid(sourceLocation.getComponentId()); + location.setFilePath(requireNonNullElse(locationComponent, component).getName()); + location.setMessage(sourceLocation.getMsg()); + + TextRange textRange = getTextRange(sourceLocation.getTextRange(), sourceLocation.getChecksum()); + location.setTextRange(textRange); + + locations.add(location); + } + flow.setLocations(locations); + flows.add(flow); + } + event.setFlows(flows); + return event; + } + + @NotNull + private static TextRange getTextRange(DbCommons.TextRange source, String checksum) { + TextRange textRange = new TextRange(); + textRange.setStartLine(source.getStartLine()); + textRange.setStartLineOffset(source.getStartOffset()); + textRange.setEndLine(source.getEndLine()); + textRange.setEndLineOffset(source.getEndOffset()); + textRange.setHash(checksum); + return textRange; + } + + private static boolean isTaintVulnerability(DefaultIssue issue) { + return TAINT_REPOSITORIES.contains(issue.getRuleKey().repository()) + && issue.getLocations() != null + && !RuleType.SECURITY_HOTSPOT.equals(issue.type()); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java new file mode 100644 index 00000000000..ce7ebc528dd --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java @@ -0,0 +1,72 @@ +/* + * 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 TextRange { + private int startLine; + private int startLineOffset; + private int endLine; + private int endLineOffset; + private String hash; + + public TextRange() { + // nothing to do + } + + public int getStartLine() { + return startLine; + } + + public void setStartLine(int startLine) { + this.startLine = startLine; + } + + public int getStartLineOffset() { + return startLineOffset; + } + + public void setStartLineOffset(int startLineOffset) { + this.startLineOffset = startLineOffset; + } + + public int getEndLine() { + return endLine; + } + + public void setEndLine(int endLine) { + this.endLine = endLine; + } + + public int getEndLineOffset() { + return endLineOffset; + } + + public void setEndLineOffset(int endLineOffset) { + this.endLineOffset = endLineOffset; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/package-info.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/package-info.java new file mode 100644 index 00000000000..0f3e17f8999 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.ce.task.projectanalysis.pushevent; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStep.java new file mode 100644 index 00000000000..97ab977542a --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStep.java @@ -0,0 +1,103 @@ +/* + * 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.step; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.Collection; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; +import org.sonar.ce.task.projectanalysis.pushevent.PushEvent; +import org.sonar.ce.task.projectanalysis.pushevent.PushEventRepository; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.pushevent.PushEventDto; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class PersistPushEventsStep implements ComputationStep { + private static final int MAX_BATCH_SIZE = 250; + private static final Logger LOGGER = Loggers.get(PersistPushEventsStep.class); + private static final Gson GSON = new GsonBuilder().create(); + + private final DbClient dbClient; + private final PushEventRepository pushEventRepository; + private final TreeRootHolder treeRootHolder; + + public PersistPushEventsStep(DbClient dbClient, PushEventRepository pushEventRepository, + TreeRootHolder treeRootHolder) { + this.dbClient = dbClient; + this.pushEventRepository = pushEventRepository; + this.treeRootHolder = treeRootHolder; + } + + @Override + public void execute(Context context) { + Collection<PushEvent<?>> issues = pushEventRepository.getEvents(); + if (issues.isEmpty()) { + return; + } + + try (DbSession dbSession = dbClient.openSession(true)) { + int batchCounter = 0; + for (PushEvent<?> event : issues) { + pushEvent(dbSession, event); + batchCounter++; + batchCounter = flushIfNeeded(dbSession, batchCounter); + } + flushSession(dbSession); + + } catch (Exception ex) { + LOGGER.warn("Error during publishing push event", ex); + } + } + + private void pushEvent(DbSession dbSession, PushEvent<?> event) { + PushEventDto eventDto = new PushEventDto() + .setProjectUuid(treeRootHolder.getRoot().getUuid()) + .setPayload(serializeIssueToPushEvent(event)); + dbClient.pushEventDao().insert(dbSession, eventDto); + } + + private static byte[] serializeIssueToPushEvent(PushEvent<?> event) { + return GSON.toJson(event.getData()).getBytes(UTF_8); + } + + private static int flushIfNeeded(DbSession dbSession, int batchCounter) { + if (batchCounter > MAX_BATCH_SIZE) { + flushSession(dbSession); + batchCounter = 0; + } + return batchCounter; + } + + private static void flushSession(DbSession dbSession) { + dbSession.flushStatements(); + dbSession.commit(); + } + + @Override + public String getDescription() { + return "Publishing taint vulnerabilities events"; + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java index a67ed8c74d9..a482de93c0d 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java @@ -108,6 +108,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { PurgeDatastoresStep.class, IndexAnalysisStep.class, UpdateNeedIssueSyncStep.class, + PersistPushEventsStep.class, // notifications are sent at the end, so that webapp displays up-to-date information SendIssueNotificationsStep.class, diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImplTest.java new file mode 100644 index 00000000000..df9f8ecd6ff --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImplTest.java @@ -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.assertj.core.api.Assertions.tuple; + +public class PushEventRepositoryImplTest { + + private final PushEventRepository underTest = new PushEventRepositoryImpl(); + + @Test + public void should_add_event_to_repository() { + assertThat(underTest.getEvents()).isEmpty(); + + underTest.add(new PushEvent<>().setName("event1").setData("event_data1")); + underTest.add(new PushEvent<>().setName("event2").setData("event_data2")); + + assertThat(underTest.getEvents()) + .extracting(PushEvent::getData, PushEvent::getName) + .containsExactlyInAnyOrder(tuple("event_data1", "event1"), + tuple("event_data2", "event2")); + + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitorTest.java new file mode 100644 index 00000000000..ca588186539 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitorTest.java @@ -0,0 +1,196 @@ +/* + * 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 java.util.Date; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleType; +import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; +import org.sonar.ce.task.projectanalysis.analysis.TestBranch; +import org.sonar.ce.task.projectanalysis.component.Component; +import org.sonar.ce.task.projectanalysis.component.Component.Status; +import org.sonar.ce.task.projectanalysis.component.Component.Type; +import org.sonar.ce.task.projectanalysis.component.ComponentImpl; +import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; +import org.sonar.ce.task.projectanalysis.component.ReportAttributes; +import org.sonar.ce.task.projectanalysis.component.ReportComponent; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.db.protobuf.DbCommons; +import org.sonar.db.protobuf.DbIssues; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class TaintVulnerabilityVisitorTest { + + private final PushEventRepository repositoryMock = mock(PushEventRepository.class); + @Rule + public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); + @Rule + public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule() + .setBranch(new TestBranch("develop")); + + private final TaintVulnerabilityVisitor underTest = new TaintVulnerabilityVisitor(repositoryMock, analysisMetadataHolder, treeRootHolder); + + @Test + public void add_event_to_repository_if_taint_vulnerability_is_new() { + buildComponentTree(); + Component component = createIssueComponent(); + DefaultIssue defaultIssue = createDefaultIssue() + .setNew(true); + + underTest.onIssue(component, defaultIssue); + + verify(repositoryMock).add(argThat(PushEventMatcher.eq(new PushEvent<>().setName("TaintVulnerabilityRaised")))); + } + + @Test + public void add_event_to_repository_if_taint_vulnerability_is_copied() { + buildComponentTree(); + Component component = createIssueComponent(); + DefaultIssue defaultIssue = createDefaultIssue() + .setCopied(true); + + underTest.onIssue(component, defaultIssue); + + verify(repositoryMock).add(argThat(PushEventMatcher.eq(new PushEvent<>().setName("TaintVulnerabilityRaised")))); + } + + @Test + public void skip_issue_if_issue_changed() { + Component component = createIssueComponent(); + + DefaultIssue defaultIssue = new DefaultIssue() + .setChanged(true) + .setType(RuleType.VULNERABILITY) + .setRuleKey(RuleKey.of("javasecurity", "S123")); + + underTest.onIssue(component, defaultIssue); + + verify(repositoryMock, times(0)).add(any(PushEvent.class)); + } + + @Test + public void skip_if_issue_not_from_taint_vulnerability_repository() { + Component component = createIssueComponent(); + + DefaultIssue defaultIssue = new DefaultIssue() + .setChanged(true) + .setType(RuleType.VULNERABILITY) + .setRuleKey(RuleKey.of("weirdrepo", "S123")); + + underTest.onIssue(component, defaultIssue); + + verify(repositoryMock, times(0)).add(any(PushEvent.class)); + } + + @Test + public void skip_if_issue_is_a_hotspot() { + Component component = createIssueComponent(); + + DefaultIssue defaultIssue = new DefaultIssue() + .setChanged(true) + .setType(RuleType.SECURITY_HOTSPOT) + .setRuleKey(RuleKey.of("javasecurity", "S123")); + + underTest.onIssue(component, defaultIssue); + + verify(repositoryMock, times(0)).add(any(PushEvent.class)); + } + + @Test + public void skip_if_issue_does_not_have_locations() { + Component component = createIssueComponent(); + + DefaultIssue defaultIssue = new DefaultIssue() + .setChanged(true) + .setType(RuleType.VULNERABILITY) + .setRuleKey(RuleKey.of("javasecurity", "S123")); + + underTest.onIssue(component, defaultIssue); + + verify(repositoryMock, times(0)).add(any(PushEvent.class)); + } + + private void buildComponentTree() { + treeRootHolder.setRoot(ReportComponent.builder(Type.PROJECT, 1) + .setUuid("uuid_1") + .addChildren(ReportComponent.builder(Type.FILE, 2) + .setUuid("issue-component-uuid") + .build()) + .addChildren(ReportComponent.builder(Type.FILE, 3) + .setUuid("location-component-uuid") + .build()) + .build()); + } + + private ComponentImpl createIssueComponent() { + return ComponentImpl.builder(Type.FILE) + .setReportAttributes(mock(ReportAttributes.class)) + .setUuid("issue-component-uuid") + .setDbKey("issue-component-key") + .setName("project/SomeClass.java") + .setStatus(Status.ADDED) + .build(); + } + + private DefaultIssue createDefaultIssue() { + return new DefaultIssue() + .setType(RuleType.VULNERABILITY) + .setCreationDate(new Date()) + .setLocations(DbIssues.Locations.newBuilder() + .addFlow(DbIssues.Flow.newBuilder() + .addLocation(DbIssues.Location.newBuilder() + .setChecksum("checksum") + .setComponentId("location-component-uuid") + .build()) + .build()) + .setTextRange(DbCommons.TextRange.newBuilder() + .setStartLine(1) + .build()) + .build()) + .setRuleKey(RuleKey.of("javasecurity", "S123")); + } + + private static class PushEventMatcher implements ArgumentMatcher<PushEvent<?>> { + + private final PushEvent<?> left; + + static PushEventMatcher eq(PushEvent<?> left) { + return new PushEventMatcher(left); + } + + private PushEventMatcher(PushEvent<?> left) { + this.left = left; + } + + @Override + public boolean matches(PushEvent<?> right) { + return left.getName().equals(right.getName()); + } + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStepTest.java new file mode 100644 index 00000000000..f1b3a051bd9 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStepTest.java @@ -0,0 +1,118 @@ +/* + * 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.step; + +import java.util.stream.IntStream; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.impl.utils.TestSystem2; +import org.sonar.ce.task.projectanalysis.component.Component.Type; +import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; +import org.sonar.ce.task.projectanalysis.component.ReportComponent; +import org.sonar.ce.task.projectanalysis.pushevent.PushEvent; +import org.sonar.ce.task.projectanalysis.pushevent.PushEventRepository; +import org.sonar.ce.task.projectanalysis.pushevent.PushEventRepositoryImpl; +import org.sonar.ce.task.step.ComputationStep; +import org.sonar.db.DbTester; +import org.sonar.db.pushevent.PushEventDto; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class PersistPushEventsStepTest { + + private final TestSystem2 system2 = new TestSystem2().setNow(1L); + + @Rule + public DbTester db = DbTester.create(system2); + @Rule + public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); + + private final PushEventRepository pushEventRepository = new PushEventRepositoryImpl(); + + private final PersistPushEventsStep underTest = new PersistPushEventsStep(db.getDbClient(), pushEventRepository, treeRootHolder); + + @Before + public void before() { + treeRootHolder.setRoot(ReportComponent.builder(Type.PROJECT, 1) + .setUuid("uuid_1") + .build()); + } + + @Test + public void store_push_events() { + pushEventRepository.add(new PushEvent<>().setName("event1").setData("data1")); + pushEventRepository.add(new PushEvent<>().setName("event2").setData("data2")); + pushEventRepository.add(new PushEvent<>().setName("event3").setData("data3")); + + underTest.execute(mock(ComputationStep.Context.class)); + + assertThat(db.countSql(db.getSession(), "SELECT count(uuid) FROM push_events")).isEqualTo(3); + } + + @Test + public void event_data_should_be_serialized() { + system2.setNow(1L); + pushEventRepository.add(new PushEvent<>().setName("event1").setData(new Data().setEventData("something"))); + + underTest.execute(mock(ComputationStep.Context.class)); + + assertThat(db.getDbClient().pushEventDao().selectByUuid(db.getSession(), "1")) + .extracting(PushEventDto::getUuid, PushEventDto::getProjectUuid, PushEventDto::getPayload, PushEventDto::getCreatedAt) + .containsExactly("1", "uuid_1", "{\"eventData\":\"something\"}".getBytes(UTF_8), 1L); + } + + @Test + public void store_push_events_in_batches() { + IntStream.range(1, 252) + .forEach(value -> pushEventRepository.add(new PushEvent<>().setName("event" + value).setData("data" + value))); + + underTest.execute(mock(ComputationStep.Context.class)); + + assertThat(db.countSql(db.getSession(), "SELECT count(uuid) FROM push_events")).isEqualTo(251); + } + + @Test + public void skip_persist_if_no_push_events() { + underTest.execute(mock(ComputationStep.Context.class)); + + assertThat(db.countSql(db.getSession(), "SELECT count(uuid) FROM push_events")).isZero(); + } + + private static class Data { + private String eventData; + + public Data() { + // nothing to do + } + + public String getEventData() { + return eventData; + } + + public Data setEventData(String eventData) { + this.eventData = eventData; + return this; + } + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java index 226c7ac60d8..efaca113b35 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java @@ -24,14 +24,13 @@ import java.util.Date; import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; -import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; -import org.sonar.ce.task.projectanalysis.analysis.Branch; +import org.sonar.ce.task.projectanalysis.analysis.TestBranch; import org.sonar.ce.task.projectanalysis.component.BranchPersister; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; @@ -42,16 +41,12 @@ import org.sonar.ce.task.projectanalysis.component.ReportComponent; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; import org.sonar.ce.task.step.ComputationStep; import org.sonar.ce.task.step.TestComputationStepContext; -import org.sonar.core.component.ComponentKeys; import org.sonar.db.DbClient; import org.sonar.db.DbTester; -import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.server.project.Project; -import static org.apache.commons.lang.StringUtils.isEmpty; -import static org.apache.commons.lang.StringUtils.trimToNull; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; @@ -645,54 +640,4 @@ public class ReportPersistComponentsStepTest extends BaseStepTest { }; } - private static class TestBranch implements Branch { - private final String name; - - public TestBranch(String name) { - this.name = name; - } - - @Override - public BranchType getType() { - return BranchType.BRANCH; - } - - @Override - public boolean isMain() { - return false; - } - - @Override - public String getReferenceBranchUuid() { - throw new UnsupportedOperationException(); - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean supportsCrossProjectCpd() { - return false; - } - - @Override - public String getPullRequestKey() { - throw new UnsupportedOperationException(); - } - - @Override - public String getTargetBranchName() { - throw new UnsupportedOperationException(); - } - - @Override - public String generateKey(String projectKey, @Nullable String fileOrDirPath) { - if (isEmpty(fileOrDirPath)) { - return projectKey; - } - return ComponentKeys.createEffectiveKey(projectKey, trimToNull(fileOrDirPath)); - } - } } diff --git a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/TestBranch.java b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/TestBranch.java new file mode 100644 index 00000000000..c906799e496 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/TestBranch.java @@ -0,0 +1,78 @@ +/* + * 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.analysis; + +import javax.annotation.Nullable; +import org.sonar.core.component.ComponentKeys; +import org.sonar.db.component.BranchType; + +import static org.apache.commons.lang.StringUtils.isEmpty; +import static org.apache.commons.lang.StringUtils.trimToNull; + +public class TestBranch implements Branch { + private final String name; + + public TestBranch(String name) { + this.name = name; + } + + @Override + public BranchType getType() { + return BranchType.BRANCH; + } + + @Override + public boolean isMain() { + return false; + } + + @Override + public String getReferenceBranchUuid() { + throw new UnsupportedOperationException(); + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean supportsCrossProjectCpd() { + return false; + } + + @Override + public String getPullRequestKey() { + throw new UnsupportedOperationException(); + } + + @Override + public String getTargetBranchName() { + throw new UnsupportedOperationException(); + } + + @Override + public String generateKey(String projectKey, @Nullable String fileOrDirPath) { + if (isEmpty(fileOrDirPath)) { + return projectKey; + } + return ComponentKeys.createEffectiveKey(projectKey, trimToNull(fileOrDirPath)); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderRule.java b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderRule.java index fc835571c23..ffe4830db1b 100644 --- a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderRule.java +++ b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderRule.java @@ -99,6 +99,11 @@ public class TreeRootHolderRule extends ExternalResource implements TreeRootHold } @Override + public Component getComponentByUuid(String uuid) { + return delegate.getComponentByUuid(uuid); + } + + @Override public Optional<Component> getOptionalComponentByRef(int ref) { return delegate.getOptionalComponentByRef(ref); } |