Skip to content

Commit

Permalink
Azure DevOps : Support for Config-as-code and Auto Profiling. (#394)
Browse files Browse the repository at this point in the history
* Added support for config as code in ADO.

* Added support for Auto Profiling within ADO.
  • Loading branch information
Mithilesh Pawar authored Aug 20, 2020
1 parent f2275bd commit 26dc664
Show file tree
Hide file tree
Showing 9 changed files with 416 additions and 74 deletions.
51 changes: 34 additions & 17 deletions src/main/java/com/checkmarx/flow/controller/ADOController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@
import com.checkmarx.flow.config.ADOProperties;
import com.checkmarx.flow.config.FlowProperties;
import com.checkmarx.flow.config.JiraProperties;
import com.checkmarx.flow.dto.*;
import com.checkmarx.flow.dto.BugTracker;
import com.checkmarx.flow.dto.ControllerRequest;
import com.checkmarx.flow.dto.EventResponse;
import com.checkmarx.flow.dto.ScanRequest;
import com.checkmarx.flow.dto.azure.*;
import com.checkmarx.flow.exception.InvalidTokenException;
import com.checkmarx.flow.service.ConfigurationOverrider;
import com.checkmarx.flow.service.FilterFactory;
import com.checkmarx.flow.service.FlowService;
import com.checkmarx.flow.service.HelperService;
import com.checkmarx.flow.service.*;
import com.checkmarx.flow.utils.HTMLHelper;
import com.checkmarx.flow.utils.ScanUtils;
import com.checkmarx.sdk.config.CxProperties;
import com.checkmarx.sdk.dto.CxConfig;
import com.checkmarx.sdk.dto.filtering.FilterConfiguration;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -27,6 +28,7 @@
/**
* Handles Azure DevOps (ADO) webhook requests.
*/
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/")
Expand All @@ -37,7 +39,6 @@ public class ADOController extends AdoControllerBase {
private static final String AUTHORIZATION = "authorization";
private static final int NAMESPACE_INDEX = 3;
private static final String EMPTY_STRING = "";
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ADOController.class);
private final ADOProperties properties;
private final FlowProperties flowProperties;
private final CxProperties cxProperties;
Expand All @@ -46,6 +47,7 @@ public class ADOController extends AdoControllerBase {
private final HelperService helperService;
private final FilterFactory filterFactory;
private final ConfigurationOverrider configOverrider;
private final ADOService adoService;

/**
* Pull Request event submitted (JSON)
Expand Down Expand Up @@ -74,8 +76,6 @@ public ResponseEntity<EventResponse> pullRequest(
.build());
}

FlowOverride o = ScanUtils.getMachinaOverride(controllerRequest.getOverride());

try {
Resource resource = body.getResource();
Repository repository = resource.getRepository();
Expand Down Expand Up @@ -103,7 +103,6 @@ public ResponseEntity<EventResponse> pullRequest(

initAdoSpecificParams(adoDetailsRequest);


if (StringUtils.isEmpty(product)) {
product = ScanRequest.Product.CX.getProduct();
}
Expand Down Expand Up @@ -156,10 +155,11 @@ public ResponseEntity<EventResponse> pullRequest(
.filter(filter)
.build();

request = configOverrider.overrideScanRequestProperties(o, request);
request.putAdditionalMetadata(ADOService.PROJECT_SELF_URL, getTheProjectURL(body.getResourceContainers()));
fillRequestWithAdditionalData(request, repository, body.toString());
checkForConfigAsCode(request);
request.putAdditionalMetadata("statuses_url", pullUrl.concat("/statuses"));
addMetadataToScanRequest(adoDetailsRequest, request);
request.putAdditionalMetadata(HTMLHelper.WEB_HOOK_PAYLOAD, body.toString());
request.setId(uid);
//only initiate scan/automation if target branch is applicable
if (helperService.isBranch2Scan(request, branches)) {
Expand All @@ -173,6 +173,8 @@ public ResponseEntity<EventResponse> pullRequest(
return getSuccessMessage();
}



/**
* Push Request event submitted (JSON), along with the Product (cx for example)
*/
Expand All @@ -193,8 +195,6 @@ public ResponseEntity<EventResponse> pushRequest(
adoDetailsRequest = ensureDetailsNotNull(adoDetailsRequest);
ResourceContainers resourceContainers = body.getResourceContainers();

FlowOverride o = ScanUtils.getMachinaOverride(controllerRequest.getOverride());

try {
Resource resource = body.getResource();
Repository repository = resource.getRepository();
Expand Down Expand Up @@ -277,11 +277,11 @@ public ResponseEntity<EventResponse> pushRequest(
.filter(filter)
.build();

request.putAdditionalMetadata(ADOService.PROJECT_SELF_URL, getTheProjectURL(body.getResourceContainers()));
addMetadataToScanRequest(adoDetailsRequest, request);
request.putAdditionalMetadata(HTMLHelper.WEB_HOOK_PAYLOAD, body.toString());
fillRequestWithAdditionalData(request,repository, body.toString());
//if an override blob/file is provided, substitute these values
request = configOverrider.overrideScanRequestProperties(o, request);

checkForConfigAsCode(request);
request.setId(uid);
//only initiate scan/automation if target branch is applicable
if (helperService.isBranch2Scan(request, branches)) {
Expand Down Expand Up @@ -354,4 +354,21 @@ private void initAdoSpecificParams(AdoDetailsRequest request) {
request.setAdoClosed(properties.getClosedStatus());
}
}

private void checkForConfigAsCode(ScanRequest request) {
CxConfig cxConfig = adoService.getCxConfigOverride(request);
configOverrider.overrideScanRequestProperties(cxConfig, request);
}

private void fillRequestWithAdditionalData(ScanRequest request, Repository repository, String hookPayload) {
request.putAdditionalMetadata(ADOService.REPO_ID, repository.getId());
request.putAdditionalMetadata(ADOService.REPO_SELF_URL, repository.getUrl());
request.putAdditionalMetadata(HTMLHelper.WEB_HOOK_PAYLOAD, hookPayload);
}

private String getTheProjectURL(ResourceContainers resourceContainers) {
String projectId = resourceContainers.getProject().getId();
String baseUrl = resourceContainers.getProject().getBaseUrl();
return baseUrl.concat(projectId);
}
}
26 changes: 8 additions & 18 deletions src/main/java/com/checkmarx/flow/custom/ADOIssueTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.checkmarx.flow.dto.ScanRequest;
import com.checkmarx.flow.dto.azure.CreateWorkItemAttr;
import com.checkmarx.flow.exception.MachinaException;
import com.checkmarx.flow.utils.ADOUtils;
import com.checkmarx.flow.utils.HTMLHelper;
import com.checkmarx.flow.utils.ScanUtils;
import com.checkmarx.sdk.config.Constants;
Expand Down Expand Up @@ -151,7 +152,7 @@ else if(!ScanUtils.empty(request.getApplication())){
log.debug(wiq);
JSONObject wiqJson = new JSONObject();
wiqJson.put("query", wiq);
HttpEntity<String> httpEntity = new HttpEntity<>(wiqJson.toString(), createAuthHeaders());
HttpEntity<String> httpEntity = new HttpEntity<>(wiqJson.toString(), ADOUtils.createAuthHeaders(properties.getToken()));

ResponseEntity<String> response = restTemplate.exchange(endpoint,
HttpMethod.POST, httpEntity, String.class);
Expand All @@ -174,7 +175,7 @@ else if(!ScanUtils.empty(request.getApplication())){
}

private Issue getIssue(String uri, String issueBody){
HttpEntity<Void> httpEntity = new HttpEntity<>(createAuthHeaders());
HttpEntity<Void> httpEntity = new HttpEntity<>(ADOUtils.createAuthHeaders(properties.getToken()));
log.debug("Getting issue at uri {}", uri);
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, String.class);
String r = response.getBody();
Expand Down Expand Up @@ -253,7 +254,7 @@ else if(!ScanUtils.empty(request.getApplication())){
}

log.debug("Request body: {}", body);
HttpEntity<List<CreateWorkItemAttr>> httpEntity = new HttpEntity<>(body, createPatchAuthHeaders());
HttpEntity<List<CreateWorkItemAttr>> httpEntity = new HttpEntity<>(body, ADOUtils.createPatchAuthHeaders(properties.getToken()));

ResponseEntity<String> response = restTemplate.exchange(endpoint, HttpMethod.POST, httpEntity, String.class);
try {
Expand Down Expand Up @@ -354,7 +355,7 @@ public void closeIssue(Issue issue, ScanRequest request) {

List<CreateWorkItemAttr> body = new ArrayList<>(Collections.singletonList(state));

HttpEntity<List<CreateWorkItemAttr>> httpEntity = new HttpEntity<>(body, createPatchAuthHeaders());
HttpEntity<List<CreateWorkItemAttr>> httpEntity = new HttpEntity<>(body, ADOUtils.createPatchAuthHeaders(properties.getToken()));

restTemplate.exchange(endpoint, HttpMethod.PATCH, httpEntity, String.class);
}
Expand All @@ -378,7 +379,7 @@ public Issue updateIssue(Issue issue, ScanResults.XIssue resultIssue, ScanReques

List<CreateWorkItemAttr> body = new ArrayList<>(Arrays.asList(state, description));

HttpEntity<List<CreateWorkItemAttr>> httpEntity = new HttpEntity<>(body, createPatchAuthHeaders());
HttpEntity<List<CreateWorkItemAttr>> httpEntity = new HttpEntity<>(body, ADOUtils.createPatchAuthHeaders(properties.getToken()));

restTemplate.exchange(endpoint, HttpMethod.PATCH, httpEntity, String.class);
return getIssue(issue.getUrl(), issueBody);
Expand Down Expand Up @@ -430,18 +431,7 @@ public void complete(ScanRequest request, ScanResults results) {
log.info("Finalizing Azure Processing");
}

private HttpHeaders createAuthHeaders(){
String encoding = Base64.getEncoder().encodeToString(":".concat(properties.getToken()).getBytes());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type", "application/json");
httpHeaders.set("Authorization", "Basic ".concat(encoding));
httpHeaders.set("Accept", "application/json");
return httpHeaders;
}

private HttpHeaders createPatchAuthHeaders(){
HttpHeaders httpHeaders = createAuthHeaders();
httpHeaders.set("Content-Type", "application/json-patch+json");
return httpHeaders;
}


}
40 changes: 40 additions & 0 deletions src/main/java/com/checkmarx/flow/dto/azure/Content.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.checkmarx.flow.dto.azure;

import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"count",
"value"
})
public class Content {

@JsonProperty("count")
private Integer count;
@JsonProperty("value")
private List<Value> value = null;

@JsonProperty("count")
public Integer getCount() {
return count;
}

@JsonProperty("count")
public void setCount(Integer count) {
this.count = count;
}

@JsonProperty("value")
public List<Value> getValue() {
return value;
}

@JsonProperty("value")
public void setValue(List<Value> value) {
this.value = value;
}

}
91 changes: 91 additions & 0 deletions src/main/java/com/checkmarx/flow/dto/azure/Value.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.checkmarx.flow.dto.azure;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"objectId",
"gitObjectType",
"commitId",
"path",
"isFolder",
"url"
})
public class Value {

@JsonProperty("objectId")
private String objectId;
@JsonProperty("gitObjectType")
private String gitObjectType;
@JsonProperty("commitId")
private String commitId;
@JsonProperty("path")
private String path;
@JsonProperty("isFolder")
private boolean isFolder;
@JsonProperty("url")
private String url;

@JsonProperty("objectId")
public String getObjectId() {
return objectId;
}

@JsonProperty("objectId")
public void setObjectId(String objectId) {
this.objectId = objectId;
}

@JsonProperty("gitObjectType")
public String getGitObjectType() {
return gitObjectType;
}

@JsonProperty("gitObjectType")
public void setGitObjectType(String gitObjectType) {
this.gitObjectType = gitObjectType;
}

@JsonProperty("commitId")
public String getCommitId() {
return commitId;
}

@JsonProperty("commitId")
public void setCommitId(String commitId) {
this.commitId = commitId;
}

@JsonProperty("path")
public String getPath() {
return path;
}

@JsonProperty("path")
public void setPath(String path) {
this.path = path;
}

@JsonProperty("isFolder")
public boolean getIsFolder() {
return isFolder;
}

@JsonProperty("isFolder")
public void setIsFolder(boolean isFolder) {
this.isFolder = isFolder;
}

@JsonProperty("url")
public String getUrl() {
return url;
}

@JsonProperty("url")
public void setUrl(String url) {
this.url = url;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import com.checkmarx.flow.config.FlowProperties;
import com.checkmarx.flow.dto.ScanRequest;
import com.checkmarx.flow.dto.Sources;
import com.checkmarx.flow.service.BitBucketService;
import com.checkmarx.flow.service.GitHubService;
import com.checkmarx.flow.service.GitLabService;
import com.checkmarx.flow.service.HelperService;
import com.checkmarx.flow.service.*;
import com.checkmarx.flow.utils.ScanUtils;
import com.checkmarx.sdk.config.Constants;
import com.checkmarx.sdk.config.CxProperties;
Expand Down Expand Up @@ -36,6 +33,7 @@ public class ScanRequestConverter {
private final GitHubService gitService;
private final GitLabService gitLabService;
private final BitBucketService bitBucketService;
private final ADOService adoService;

public CxScanParams toScanParams(ScanRequest scanRequest) throws CheckmarxException {
String ownerId = determineTeamAndOwnerID(scanRequest);
Expand Down Expand Up @@ -164,7 +162,7 @@ private Sources getRepoContent(ScanRequest request) {
sources = bitBucketService.getRepoContent(request);
break;
case ADO:
log.warn("Profiling is not available for Azure DevOps");
sources = adoService.getRepoContent(request);
break;
default:
log.info("Nothing to profile");
Expand Down
Loading

0 comments on commit 26dc664

Please sign in to comment.