]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17279 - Add ITs for scanning alerts in GitHub enterprise
authorKlaudio Sinani <klaudio.sinani@sonarsource.com>
Thu, 15 Sep 2022 11:11:10 +0000 (13:11 +0200)
committerPhilippe Perrin <philippe.perrin@sonarsource.com>
Fri, 7 Oct 2022 10:10:21 +0000 (12:10 +0200)
server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubApplicationHttpClientImpl.java
server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/SonarQubeIssueKeyFormatter.java [new file with mode: 0644]
server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/SonarQubeIssueKeyFormatterTest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpConnector.java
sonar-ws/src/main/java/org/sonarqube/ws/client/PostRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/almintegrations/AlmIntegrationsService.java
sonar-ws/src/main/java/org/sonarqube/ws/client/almintegrations/SendGithubCodeScanningAlertWebhookPayloadRequest.java [new file with mode: 0644]
sonar-ws/src/test/java/org/sonarqube/ws/client/HttpConnectorTest.java
sonar-ws/src/test/java/org/sonarqube/ws/client/PostRequestTest.java

index 96045882a0b14f25f22628741241bf704dcbdc95..118453091428d1e9ba75e28f0b53378dc7ccf784 100644 (file)
@@ -79,7 +79,6 @@ public class GithubApplicationHttpClientImpl implements GithubApplicationHttpCli
 
   private GetResponse get(String appUrl, AccessToken token, String endPoint, boolean withLog) throws IOException {
     validateEndPoint(endPoint);
-
     try (okhttp3.Response response = client.newCall(newGetRequest(appUrl, token, endPoint)).execute()) {
       int responseCode = response.code();
       if (responseCode != HTTP_OK) {
diff --git a/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/SonarQubeIssueKeyFormatter.java b/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/SonarQubeIssueKeyFormatter.java
new file mode 100644 (file)
index 0000000..f14ca04
--- /dev/null
@@ -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.alm.client.github;
+
+import java.util.Optional;
+
+import static org.apache.commons.lang.StringUtils.substringBetween;
+
+
+public final class SonarQubeIssueKeyFormatter {
+  static final String SONAR_ISSUE_KEY_PREFIX = "<!--SONAR_ISSUE_KEY:";
+  static final String SONAR_ISSUE_KEY_SUFFIX = "-->";
+
+  private SonarQubeIssueKeyFormatter() {
+  }
+
+  public static String serialize(String key) {
+    return SONAR_ISSUE_KEY_PREFIX + key + SONAR_ISSUE_KEY_SUFFIX;
+  }
+
+  public static Optional<String> deserialize(String messageText) {
+    return Optional.ofNullable(substringBetween(messageText, SONAR_ISSUE_KEY_PREFIX, SONAR_ISSUE_KEY_SUFFIX));
+  }
+}
diff --git a/server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/SonarQubeIssueKeyFormatterTest.java b/server/sonar-alm-client/src/test/java/org/sonar/alm/client/github/SonarQubeIssueKeyFormatterTest.java
new file mode 100644 (file)
index 0000000..04bcaa3
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.alm.client.github;
+
+import java.util.Optional;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Test;
+
+import static java.lang.String.join;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.alm.client.github.SonarQubeIssueKeyFormatter.SONAR_ISSUE_KEY_PREFIX;
+import static org.sonar.alm.client.github.SonarQubeIssueKeyFormatter.SONAR_ISSUE_KEY_SUFFIX;
+
+public class SonarQubeIssueKeyFormatterTest {
+
+  @Test
+  public void should_serializeIssueKey() {
+    String issueKey = RandomStringUtils.randomAlphanumeric(20);
+
+    String serialized = SonarQubeIssueKeyFormatter.serialize(issueKey);
+
+    String expectedSerializedKey = join("", SONAR_ISSUE_KEY_PREFIX, issueKey, SONAR_ISSUE_KEY_SUFFIX);
+    assertThat(serialized).isEqualTo(expectedSerializedKey);
+  }
+
+  @Test
+  public void should_deserializeIssueKey() {
+    String issueKey = RandomStringUtils.randomAlphanumeric(20);
+    String message = join("", SONAR_ISSUE_KEY_PREFIX, issueKey, SONAR_ISSUE_KEY_SUFFIX, "a message");
+
+    Optional<String> deserialized = SonarQubeIssueKeyFormatter.deserialize(message);
+
+    assertThat(deserialized).hasValue(issueKey);
+  }
+
+  @Test
+  public void should_notDeserializeIssueKey_when_messageHasWrongFormat() {
+    String issueKey = RandomStringUtils.randomAlphanumeric(20);
+    String messageWithoutSuffix = join("", SONAR_ISSUE_KEY_PREFIX, issueKey, "a message");
+    String messageWithoutPrefix = join("", issueKey, SONAR_ISSUE_KEY_SUFFIX, "a message");
+    String messageWithPrefixSuffixReversed = join("", SONAR_ISSUE_KEY_SUFFIX, issueKey, SONAR_ISSUE_KEY_PREFIX, "a message");
+    String messageWithNoPrefixSuffix = join("", issueKey, "a message");
+
+    assertThat(SonarQubeIssueKeyFormatter.deserialize(messageWithoutSuffix)).isEmpty();
+    assertThat(SonarQubeIssueKeyFormatter.deserialize(messageWithoutPrefix)).isEmpty();
+    assertThat(SonarQubeIssueKeyFormatter.deserialize(messageWithPrefixSuffixReversed)).isEmpty();
+    assertThat(SonarQubeIssueKeyFormatter.deserialize(messageWithNoPrefixSuffix)).isEmpty();
+  }
+}
index 9ce4db76bc8b721a400ac31ff55e94fff66afdca..9442cf30f339d8341f7abfd0fafc0956b008c50c 100644 (file)
@@ -55,6 +55,7 @@ public class HttpConnector implements WsConnector {
 
   public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30_000;
   public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60_000;
+  private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
 
   /**
    * Base URL with trailing slash, for instance "https://localhost/sonarqube/".
@@ -129,7 +130,9 @@ public class HttpConnector implements WsConnector {
 
     RequestBody body;
     Map<String, PostRequest.Part> parts = postRequest.getParts();
-    if (parts.isEmpty()) {
+    if (postRequest.hasBody()) {
+      body = RequestBody.create(JSON, postRequest.getBody());
+    } else if (parts.isEmpty()) {
       // parameters are defined in the body (application/x-www-form-urlencoded)
       FormBody.Builder formBody = new FormBody.Builder();
       postRequest.getParameters().getKeys()
index 0ca678613855a31634ea4042a0406b54d8ac7b14..fd807e333fc91e1646aba0ea992a3eb71f6752f8 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Map;
  */
 public class PostRequest extends BaseRequest<PostRequest> {
 
+  private String body;
   private final Map<String, Part> parts = new LinkedHashMap<>();
 
   public PostRequest(String path) {
@@ -39,6 +40,19 @@ public class PostRequest extends BaseRequest<PostRequest> {
     return Method.POST;
   }
 
+  public PostRequest setBody(String body) {
+    this.body = body;
+    return this;
+  }
+
+  public String getBody() {
+    return body;
+  }
+
+  public boolean hasBody() {
+    return this.body != null;
+  }
+
   public PostRequest setPart(String name, Part part) {
     this.parts.put(name, part);
     return this;
index a1ff8fffbb63c7aa855dde1adca4c26bbbe1d2e4..96a4230bcf1fff246c0185c95a6f8f97c7a1ff56 100644 (file)
@@ -197,6 +197,24 @@ public class AlmIntegrationsService extends BaseService {
       AlmIntegrations.SearchBitbucketcloudReposWsResponse.parser());
   }
 
+  /**
+   * This is part of the internal API.
+   * This is a POST request.
+   *
+   * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/alm_integrations/webhook_github">Further information about this action online (including a response example)</a>
+   * @since 9.7
+   */
+  public void sendGitubCodeScanningAlertWebhookPayload(SendGithubCodeScanningAlertWebhookPayloadRequest request) {
+    call(
+      new PostRequest(path("webhook_github"))
+        .setHeader("X-GitHub-Event", request.getGithubEventHeader())
+        .setHeader("X-Hub-Signature", request.getGithubSignatureHeader())
+        .setHeader("X-Hub-Signature-256", request.getGithubSignature256Header())
+        .setBody(request.getPayload())
+        .setMediaType(MediaTypes.JSON)
+    ).content();
+  }
+
   /**
    * This is part of the internal API.
    * This is a POST request.
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/almintegrations/SendGithubCodeScanningAlertWebhookPayloadRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/almintegrations/SendGithubCodeScanningAlertWebhookPayloadRequest.java
new file mode 100644 (file)
index 0000000..e7ce6ce
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.sonarqube.ws.client.almintegrations;
+
+import javax.annotation.Generated;
+
+/**
+ * This is part of the internal API.
+ * This is a POST request.
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/alm_integrations/webhook_github">Further information about this action online (including a response example)</a>
+ * @since 9.7
+ */
+@Generated("sonar-ws-generator")
+public class SendGithubCodeScanningAlertWebhookPayloadRequest {
+  private String payload;
+  private String githubEventHeader;
+  private String githubSignatureHeader;
+  private String githubSignature256Header;
+
+  /**
+   * This is a mandatory parameter.
+   */
+  public SendGithubCodeScanningAlertWebhookPayloadRequest setPayload(String payload) {
+    this.payload = payload;
+    return this;
+  }
+
+  public String getPayload() {
+    return payload;
+  }
+
+  /**
+   * This is a mandatory parameter.
+   */
+  public SendGithubCodeScanningAlertWebhookPayloadRequest setGithubEventHeader(String header) {
+    this.githubEventHeader = header;
+    return this;
+  }
+
+  public String getGithubEventHeader() {
+    return githubEventHeader;
+  }
+
+  /**
+   * This is a mandatory parameter.
+   */
+  public SendGithubCodeScanningAlertWebhookPayloadRequest setGithubSignatureHeader(String header) {
+    this.githubSignatureHeader = header;
+    return this;
+  }
+
+  public String getGithubSignatureHeader() {
+    return this.githubSignatureHeader;
+  }
+
+  /**
+   * This is a mandatory parameter.
+   */
+  public SendGithubCodeScanningAlertWebhookPayloadRequest setGithubSignature256Header(String header) {
+    this.githubSignature256Header = header;
+    return this;
+  }
+
+  public String getGithubSignature256Header() {
+    return githubSignature256Header;
+  }
+}
index cbaae7969d57052cfc8d4a46218d9f49567881b9..31786be64cf5629311224855d320d1f05485576f 100644 (file)
@@ -365,6 +365,32 @@ public class HttpConnectorTest {
     assertThat(recordedRequest.getHeader("X-Bar")).isEqualTo("barz");
   }
 
+  @Test
+  public void setBody_shouldAddCorrectlyBodyPayloadToPostRequest_whenComposingAndSendingPostRequest() throws Exception {
+    answerHelloWorld();
+
+    String xGithubEventHeader = "code_scanning_alert";
+    String xHubSignatureHeader = "x-hub-signature";
+    String xHubSignature256Header = "x-hub-signature-256";
+    String bodyRaw = "{\"state\":\"open\"}";
+
+    PostRequest request = new PostRequest("api/alm_integrations/webhook_github")
+      .setHeader("X-GitHub-Event", xGithubEventHeader)
+      .setHeader("X-Hub-Signature", xHubSignatureHeader)
+      .setHeader("X-Hub-Signature-256", xHubSignature256Header)
+      .setBody(bodyRaw)
+      .setMediaType(MediaTypes.JSON);
+
+    underTest = HttpConnector.newBuilder().url(serverUrl).build();
+    underTest.call(request);
+
+    RecordedRequest recordedRequest = server.takeRequest();
+    assertThat(recordedRequest.getHeader("X-GitHub-Event")).isEqualTo(xGithubEventHeader);
+    assertThat(recordedRequest.getHeader("X-Hub-Signature")).isEqualTo(xHubSignatureHeader);
+    assertThat(recordedRequest.getHeader("X-Hub-Signature-256")).isEqualTo(xHubSignature256Header);
+    assertThat(recordedRequest.getBody().readUtf8()).isEqualTo(bodyRaw);
+  }
+
   @Test
   public void upload_file() throws Exception {
     answerHelloWorld();
index 43845dc4b6089b81bb5fdf4a319212001cb89254..62dcd8c7c59aedd9fdb10cdf4d8becbf53ea04e7 100644 (file)
@@ -57,4 +57,14 @@ public class PostRequestTest {
     assertThat(part.getMediaType()).isEqualTo(MediaTypes.JSON);
     assertThat(part.getFile()).isSameAs(reportFile);
   }
+
+  @Test
+  public void setBody_shouldCorrectlyAddRawBodyToPostRequest() throws IOException {
+    String bodyRaw = "{\"state\":\"open\"}";
+    PostRequest request = new PostRequest("api/alm_integrations/webhook_github");
+    request.setBody(bodyRaw);
+
+    assertThat(request.getBody()).isEqualTo(bodyRaw);
+    assertThat(request.hasBody()).isTrue();
+  }
 }