aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-ce-task-projectanalysis
diff options
context:
space:
mode:
authorJacek <jacek.poreda@sonarsource.com>2022-07-11 18:24:56 +0200
committersonartech <sonartech@sonarsource.com>2022-07-25 20:03:57 +0000
commitd66a7c69cbef6ee9647ac0233d915b2863c06338 (patch)
tree4888d2b6bb5dfc7335ff235bcbbdf0b33c14d05b /server/sonar-ce-task-projectanalysis
parent31184bdc5b3c24d88f424c1625bedc36513884cc (diff)
downloadsonarqube-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')
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolder.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderImpl.java30
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Flow.java38
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/Location.java54
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEvent.java48
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepository.java30
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImpl.java41
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java113
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitor.java125
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TextRange.java72
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/package-info.java23
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStep.java103
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java1
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventRepositoryImplTest.java45
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityVisitorTest.java196
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistPushEventsStepTest.java118
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java57
-rw-r--r--server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/TestBranch.java78
-rw-r--r--server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/component/TreeRootHolderRule.java5
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);
}