diff --git a/pom.xml b/pom.xml
index 2236ef6233..bf2e377cd2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -851,6 +851,11 @@
1.4.01
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.2
+
diff --git a/results/Benchmark_1.2-ZAPweekly-20150824-18000.xml b/results/old/Benchmark_1.2-ZAPweekly-20150824-18000.xml
similarity index 100%
rename from results/Benchmark_1.2-ZAPweekly-20150824-18000.xml
rename to results/old/Benchmark_1.2-ZAPweekly-20150824-18000.xml
diff --git a/results/Benchmark_1.2-ZAPweekly-20160905.xml b/results/old/Benchmark_1.2-ZAPweekly-20160905.xml
similarity index 100%
rename from results/Benchmark_1.2-ZAPweekly-20160905.xml
rename to results/old/Benchmark_1.2-ZAPweekly-20160905.xml
diff --git a/results/Benchmark_1.2-findbugs-v3.0.1-92.xml b/results/old/Benchmark_1.2-findbugs-v3.0.1-92.xml
similarity index 100%
rename from results/Benchmark_1.2-findbugs-v3.0.1-92.xml
rename to results/old/Benchmark_1.2-findbugs-v3.0.1-92.xml
diff --git a/results/Benchmark_1.2-findsecbugs-v1.4.0-110.xml b/results/old/Benchmark_1.2-findsecbugs-v1.4.0-110.xml
similarity index 100%
rename from results/Benchmark_1.2-findsecbugs-v1.4.0-110.xml
rename to results/old/Benchmark_1.2-findsecbugs-v1.4.0-110.xml
diff --git a/results/Benchmark_1.2-findsecbugs-v1.4.3-118.xml b/results/old/Benchmark_1.2-findsecbugs-v1.4.3-118.xml
similarity index 100%
rename from results/Benchmark_1.2-findsecbugs-v1.4.3-118.xml
rename to results/old/Benchmark_1.2-findsecbugs-v1.4.3-118.xml
diff --git a/results/Benchmark_1.2-findsecbugs-v1.4.4-253.xml b/results/old/Benchmark_1.2-findsecbugs-v1.4.4-253.xml
similarity index 100%
rename from results/Benchmark_1.2-findsecbugs-v1.4.4-253.xml
rename to results/old/Benchmark_1.2-findsecbugs-v1.4.4-253.xml
diff --git a/results/Benchmark_1.2-findsecbugs-v1.4.5-129.xml b/results/old/Benchmark_1.2-findsecbugs-v1.4.5-129.xml
similarity index 100%
rename from results/Benchmark_1.2-findsecbugs-v1.4.5-129.xml
rename to results/old/Benchmark_1.2-findsecbugs-v1.4.5-129.xml
diff --git a/results/Benchmark_1.2-findsecbugs-v1.4.6-122.xml b/results/old/Benchmark_1.2-findsecbugs-v1.4.6-122.xml
similarity index 100%
rename from results/Benchmark_1.2-findsecbugs-v1.4.6-122.xml
rename to results/old/Benchmark_1.2-findsecbugs-v1.4.6-122.xml
diff --git a/results/Benchmark_1.2-pmd-v5.2.3-11.xml b/results/old/Benchmark_1.2-pmd-v5.2.3-11.xml
similarity index 100%
rename from results/Benchmark_1.2-pmd-v5.2.3-11.xml
rename to results/old/Benchmark_1.2-pmd-v5.2.3-11.xml
diff --git a/results/Benchmark_1.2-sonar-Java-Plugin-v3.14-330.xml b/results/old/Benchmark_1.2-sonar-Java-Plugin-v3.14-330.xml
similarity index 100%
rename from results/Benchmark_1.2-sonar-Java-Plugin-v3.14-330.xml
rename to results/old/Benchmark_1.2-sonar-Java-Plugin-v3.14-330.xml
diff --git a/results/Benchmark_1.2-visualcodegrepper-v2.2.0.xml b/results/old/Benchmark_1.2-visualcodegrepper-v2.2.0.xml
similarity index 100%
rename from results/Benchmark_1.2-visualcodegrepper-v2.2.0.xml
rename to results/old/Benchmark_1.2-visualcodegrepper-v2.2.0.xml
diff --git a/scripts/runSonarQube.sh b/scripts/runSonarQube.sh
index 2847ad7007..761294d373 100755
--- a/scripts/runSonarQube.sh
+++ b/scripts/runSonarQube.sh
@@ -29,10 +29,10 @@ sonar_user="admin"
sonar_default_password="admin"
sonar_password="P4ssword!!!!"
-echo "Creating temporary SonarQube instance..."
+docker pull sonarqube
+docker pull sonarsource/sonar-scanner-cli
-#docker pull sonarqube
-#docker pull sonarsource/sonar-scanner-cli
+echo "Creating temporary SonarQube instance..."
# start local sonarqube
docker run --rm -d --name "$container_name" -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true -p "$sonar_external_port:$sonar_internal_port" sonarqube
@@ -44,6 +44,7 @@ while [[ "$(curl --connect-timeout 5 --max-time 5 --retry 60 --retry-delay 0 --r
sleep 3
done
+echo ""
echo "Waiting for SonarQube to become ready..."
while [[ "$(curl --silent "$sonar_host/api/system/status" | jq -r '.status')" != "UP" ]]; do
@@ -51,6 +52,7 @@ while [[ "$(curl --silent "$sonar_host/api/system/status" | jq -r '.status')" !=
sleep 3
done
+echo ""
echo "SonarQube ready. Setting up instance..."
# change default password
@@ -82,15 +84,11 @@ while [[ "$(curl --silent -u "$sonar_token:" "$sonar_host/api/ce/component?compo
sleep 3
done
+echo ""
echo "Generating report..."
-benchmark_version=$(scripts/getBenchmarkVersion.sh)
-sonarqube_version=$(curl --silent -u "$sonar_token:" "$sonar_host/api/server/version")
-result_file="results/Benchmark_$benchmark_version-sonarqube-v$sonarqube_version.json"
-
-sonar-report --sonarurl "$sonar_host" --sonarcomponent="$sonar_project" --sonarusername "$sonar_user" --sonarpassword 'P4ssword!!!!' --allbugs --no-rules-in-report --save-report-json "$result_file"
+mvn exec:java -Dexec.mainClass="org.owasp.benchmark.report.sonarqube.SonarReport"
-echo "Result file written to $result_file"
echo "Shutting down SonarQube..."
-docker stop "$container_name"
+#docker stop "$container_name"
diff --git a/src/main/java/org/owasp/benchmark/report/sonarqube/SonarReport.java b/src/main/java/org/owasp/benchmark/report/sonarqube/SonarReport.java
new file mode 100644
index 0000000000..5498447d0f
--- /dev/null
+++ b/src/main/java/org/owasp/benchmark/report/sonarqube/SonarReport.java
@@ -0,0 +1,129 @@
+package org.owasp.benchmark.report.sonarqube;
+
+import static java.lang.String.join;
+import static java.nio.charset.Charset.defaultCharset;
+import static org.apache.commons.io.FileUtils.writeStringToFile;
+import static org.apache.commons.io.IOUtils.readLines;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.owasp.benchmark.report.sonarqube.dto.SonarQubeResult;
+
+public class SonarReport {
+ private static final String SONAR_USER = "admin";
+ private static final String SONAR_PASSWORD = "P4ssword!!!!";
+ private static final String SONAR_PROJECT = "benchmark";
+ public static final String SONAR_HOST = "ubuntu-server";
+ public static final String SONAR_PORT = "9876";
+
+ private static final int PAGE_SIZE = 500;
+
+ private static final String sonarAuth =
+ Base64.getEncoder().encodeToString((SONAR_USER + ":" + SONAR_PASSWORD).getBytes());
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ public static void main(String[] args) throws Exception {
+ String allJavaRules = String.join(",", allJavaRules());
+ List issues = new ArrayList<>();
+ List hotspots = new ArrayList<>();
+
+ forAllPagesAt(
+ "issues/search?componentKeys="
+ + SONAR_PROJECT
+ + "&types=VULNERABILITY&&rules="
+ + allJavaRules,
+ (result -> issues.addAll(result.issues)));
+ forAllPagesAt(
+ "hotspots/search?projectKey=" + SONAR_PROJECT,
+ (result -> hotspots.addAll(result.hotspots)));
+
+ writeStringToFile(
+ new File("results/" + resultFilename() + ".json"),
+ formattedJson(issues, hotspots),
+ defaultCharset());
+ }
+
+ private static String resultFilename() throws Exception {
+ return "Benchmark_" + benchmarkVersion() + "-sonarqube-v" + apiCall("server/version");
+ }
+
+ private static String benchmarkVersion() throws Exception {
+ return DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder()
+ .parse(new File("pom.xml"))
+ .getElementsByTagName("version")
+ .item(0)
+ .getTextContent();
+ }
+
+ private static Set allJavaRules() throws IOException {
+ Set javaRuleIds = new HashSet<>();
+
+ forAllPagesAt(
+ "rules/search",
+ (result) ->
+ result.rules.stream()
+ .filter(rule -> rule.ruleId.startsWith("java:"))
+ .forEach(rule -> javaRuleIds.add(rule.ruleId)));
+
+ return javaRuleIds;
+ }
+
+ private static void forAllPagesAt(String apiPath, Consumer pageHandlerCallback)
+ throws IOException {
+ int pages;
+ int page = 1;
+
+ do {
+ SonarQubeResult result =
+ objectMapper.readValue(
+ apiCall(apiPath + pagingSuffix(page, apiPath)), SonarQubeResult.class);
+
+ pages = (result.paging.resultCount / PAGE_SIZE) + 1;
+
+ pageHandlerCallback.accept(result);
+
+ page++;
+ } while ((page - 1) < pages);
+ }
+
+ private static String pagingSuffix(int page, String apiPath) {
+ return (apiPath.contains("?") ? "&" : "?") + "p=" + page + "&ps=" + PAGE_SIZE;
+ }
+
+ private static String apiCall(String apiPath) throws IOException {
+ URL url = new URL("http://" + SONAR_HOST + ":" + SONAR_PORT + "/api/" + apiPath);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Authorization", "Basic " + sonarAuth);
+
+ return join("\n", readLines(connection.getInputStream(), defaultCharset()));
+ }
+
+ private static String formattedJson(List issues, List hotspots)
+ throws JsonProcessingException {
+ String sb =
+ "{\"issues\":["
+ + join(",", issues)
+ + "],\"hotspots\":["
+ + join(",", hotspots)
+ + "]}";
+
+ return objectMapper
+ .writerWithDefaultPrettyPrinter()
+ .writeValueAsString(objectMapper.readValue(sb, Object.class));
+ }
+}
diff --git a/src/main/java/org/owasp/benchmark/report/sonarqube/dto/KeepAsJsonDeserializer.java b/src/main/java/org/owasp/benchmark/report/sonarqube/dto/KeepAsJsonDeserializer.java
new file mode 100644
index 0000000000..67678e9986
--- /dev/null
+++ b/src/main/java/org/owasp/benchmark/report/sonarqube/dto/KeepAsJsonDeserializer.java
@@ -0,0 +1,27 @@
+package org.owasp.benchmark.report.sonarqube.dto;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Credits to Roy Truelove */
+public class KeepAsJsonDeserializer extends JsonDeserializer> {
+
+ @Override
+ public List deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+ ObjectCodec codec = jp.getCodec();
+ TreeNode entries = codec.readTree(jp);
+ List result = new ArrayList<>();
+
+ for (int i = 0; i < entries.size(); i++) {
+ result.add(codec.readTree(codec.treeAsTokens(entries.get(i))).toString());
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/owasp/benchmark/report/sonarqube/dto/SonarQubeResult.java b/src/main/java/org/owasp/benchmark/report/sonarqube/dto/SonarQubeResult.java
new file mode 100644
index 0000000000..39806b0fda
--- /dev/null
+++ b/src/main/java/org/owasp/benchmark/report/sonarqube/dto/SonarQubeResult.java
@@ -0,0 +1,34 @@
+package org.owasp.benchmark.report.sonarqube.dto;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SonarQubeResult {
+
+ public Paging paging;
+
+ public List rules;
+
+ @JsonDeserialize(using = KeepAsJsonDeserializer.class)
+ public List issues;
+
+ @JsonDeserialize(using = KeepAsJsonDeserializer.class)
+ public List hotspots;
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Paging {
+
+ @JsonAlias("total")
+ public int resultCount;
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Rule {
+
+ @JsonAlias("key")
+ public String ruleId;
+ }
+}