com.cx.restclient.CxOSAClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cx-client-common Show documentation
Show all versions of cx-client-common Show documentation
Web client for interaction with Checkmarx SAST, SCA and OSA products
The newest version!
package com.cx.restclient;
import com.cx.restclient.common.Scanner;
import com.cx.restclient.common.ShragaUtils;
import com.cx.restclient.common.Waiter;
import com.cx.restclient.configuration.CxScanConfig;
import com.cx.restclient.dto.Results;
import com.cx.restclient.dto.Status;
import com.cx.restclient.exception.CxClientException;
import com.cx.restclient.exception.CxOSAException;
import com.cx.restclient.httpClient.CxHttpClient;
import com.cx.restclient.osa.dto.*;
import com.cx.restclient.osa.utils.OSAUtils;
import com.cx.restclient.sast.utils.LegacyClient;
import com.cx.restclient.sast.utils.State;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.StringEntity;
import org.slf4j.Logger;
import org.whitesource.agent.utils.CommandLineErrors;
import org.whitesource.fs.ComponentScan;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Properties;
import static com.cx.restclient.cxArm.dto.CxProviders.OPEN_SOURCE;
import static com.cx.restclient.cxArm.utils.CxARMUtils.getProjectViolatedPolicies;
import static com.cx.restclient.httpClient.utils.ContentType.CONTENT_TYPE_APPLICATION_JSON_V1;
import static com.cx.restclient.httpClient.utils.HttpClientHelper.convertToJson;
import static com.cx.restclient.osa.utils.OSAParam.*;
import static com.cx.restclient.osa.utils.OSAUtils.writeJsonToFile;
/**
* Created by Galn on 05/02/2018.
*/
public class CxOSAClient extends LegacyClient implements Scanner {
private Waiter osaWaiter;
private String scanId;
private OSAResults osaResults = new OSAResults();
public OSAScanStatus getStatus(String id) throws IOException {
return getOSAScanStatus(id);
}
public CxOSAClient(CxScanConfig config, Logger log) throws MalformedURLException {
super(config, log);
int interval = config.getOsaProgressInterval() != null ? config.getOsaProgressInterval() : 20;
int retry = config.getConnectionRetries() != null ? config.getConnectionRetries() : 3;
osaWaiter = new Waiter("CxOSA scan", interval, retry) {
@Override
public OSAScanStatus getStatus(String id) throws IOException {
return getOSAScanStatus(id);
}
@Override
public void printProgress(OSAScanStatus scanStatus) {
printOSAProgress(scanStatus, getStartTimeSec());
}
@Override
public OSAScanStatus resolveStatus(OSAScanStatus scanStatus) {
return resolveOSAStatus(scanStatus);
}
};
}
@Override
public Results init() {
OSAResults initOsaResults = new OSAResults();
try {
initiate();
} catch (CxClientException e) {
log.error(e.getMessage());
setState(State.FAILED);
initOsaResults.setException(e);
}
return initOsaResults;
}
@Override
public Results initiateScan() {
osaResults = new OSAResults();
try {
ensureProjectIdSpecified();
} catch (CxClientException e) {
log.error(e.getMessage());
setState(State.FAILED);
osaResults.setException(e);
return osaResults;
}
log.info("----------------------------------- Create CxOSA Scan:------------------------------------");
log.info("Creating OSA scan");
String osaDependenciesJson = config.getOsaDependenciesJson();
if (osaDependenciesJson == null) {
try {
osaDependenciesJson = resolveOSADependencies();
} catch (Exception e) {
log.error(e.getMessage());
setState(State.FAILED);
osaResults.setException(new CxClientException("Failed to resolve dependencies for OSA scan: " + e.getMessage(), e));
return osaResults;
}
}
if ((!validateOsaDependencies(osaDependenciesJson) && dependenciesErrorsExist()) ||
(config.isOsaFailOnError() && dependenciesErrorsExist())) {
log.error("Fail to resolve dependencies: {}", osaDependenciesJson);
List failedCommands = CommandLineErrors.getFailedCommands();
for (String fc : failedCommands) {
log.error("Failed command: {}", fc);
}
setState(State.FAILED);
osaResults.setException(new CxOSAException("Failed to resolve dependencies for OSA scan: \n" + osaDependenciesJson));
return osaResults;
} else if ((!validateOsaDependencies(osaDependenciesJson) && !dependenciesErrorsExist())) {
log.warn("No dependencies found for OSA scan");
setState(State.FAILED);
osaResults.setException(new CxOSAException("No dependencies found for OSA scan"));
return osaResults;
}
try {
scanId = sendOSAScan(osaDependenciesJson, projectId);
} catch (IOException e) {
scanId = null;
log.error(e.getMessage());
setState(State.FAILED);
osaResults.setException(new CxClientException("Error sending OSA scan request.", e));
return osaResults;
}
osaResults.setOsaProjectSummaryLink(config.getUrl(), projectId);
osaResults.setOsaScanId(scanId);
return osaResults;
}
private boolean dependenciesErrorsExist() {
return CommandLineErrors.getFailedCommands() != null && !CommandLineErrors.getFailedCommands().isEmpty();
}
private boolean validateOsaDependencies(String osaDependenciesJson) {
return StringUtils.isNotEmpty(osaDependenciesJson) && !osaDependenciesJson.equalsIgnoreCase("[{\"dependencies\":[],\"projectToken\":\" \"}]");
}
public void setOsaFSAProperties(Properties fsaConfig) { //For CxMaven plugin
config.setOsaFsaConfig(fsaConfig);
}
private String resolveOSADependencies() throws JsonProcessingException {
log.info("Scanning for CxOSA compatible files");
Properties scannerProperties = config.getOsaFsaConfig();
if (scannerProperties == null) {
scannerProperties = OSAUtils.generateOSAScanConfiguration(
config.getOsaFolderExclusions(),
config.getOsaFilterPattern(),
config.getOsaArchiveIncludePatterns(),
config.getEffectiveSourceDirForDependencyScan(),
config.getOsaRunInstall(),
config.getOsaScanDepth(),
log);
}
ObjectMapper mapper = new ObjectMapper();
log.info("Scanner properties: " + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(scannerProperties.toString()));
ComponentScan componentScan = new ComponentScan(scannerProperties);
String osaDependenciesJson = componentScan.scan();
OSAUtils.writeToOsaListToFile(OSAUtils.getWorkDirectory(config.getReportsDir(), config.getOsaGenerateJsonReport()), osaDependenciesJson, log);
return osaDependenciesJson;
}
@Override
public CxHttpClient getHttpClient() {
return httpClient;
}
@Override
public Results waitForScanResults() {
try {
ensureProjectIdSpecified();
if (scanId == null) {
throw new CxClientException("Scan was not created.");
}
log.info("-------------------------------------Get CxOSA Results:-----------------------------------");
log.info("Waiting for OSA scan to finish");
OSAScanStatus osaScanStatus;
osaScanStatus = osaWaiter.waitForTaskToFinish(scanId, this.config.getOsaScanTimeoutInMinutes(), log);
log.info("OSA scan finished successfully. Retrieving OSA scan results");
log.info("Creating OSA reports");
osaResults = retrieveOSAResults(scanId, osaScanStatus, projectId);
if (config.getEnablePolicyViolations()) {
resolveOSAViolation(osaResults, projectId);
}
OSAUtils.printOSAResultsToConsole(osaResults, config.getEnablePolicyViolations(), log);
if (config.getReportsDir() != null) {
writeJsonToFile(OSA_SUMMARY_NAME, osaResults.getResults(), config.getReportsDir(), config.getOsaGenerateJsonReport(), log);
writeJsonToFile(OSA_LIBRARIES_NAME, osaResults.getOsaLibraries(), config.getReportsDir(), config.getOsaGenerateJsonReport(), log);
writeJsonToFile(OSA_VULNERABILITIES_NAME, osaResults.getOsaVulnerabilities(), config.getReportsDir(), config.getOsaGenerateJsonReport(), log);
}
} catch (Exception e) {
log.error(e.getMessage());
osaResults.setException(new CxClientException("Failed to retrieve OSA results.", e));
}
return osaResults;
}
private OSAResults retrieveOSAResults(String scanId, OSAScanStatus osaScanStatus, long projectId) throws IOException {
OSASummaryResults osaSummaryResults = getOSAScanSummaryResults(scanId);
List osaLibraries = getOSALibraries(scanId);
List osaVulnerabilities = getOSAVulnerabilities(scanId);
OSAResults results = new OSAResults();
results.setOsaScanId(scanId);
results.setResults(osaSummaryResults, osaLibraries, osaVulnerabilities, osaScanStatus, config.getUrl(), projectId);
return results;
}
private void resolveOSAViolation(OSAResults osaResults, long projectId) {
try {
getProjectViolatedPolicies(httpClient, config.getCxARMUrl(), projectId, OPEN_SOURCE.value())
.forEach(osaResults::addPolicy);
} catch (Exception ex) {
throw new CxClientException("CxARM is not available. Policy violations for OSA cannot be calculated: " + ex.getMessage());
}
}
@Override
public Results getLatestScanResults() {
osaResults = new OSAResults();
try {
ensureProjectIdSpecified();
log.info("----------------------------------Get CxOSA Last Results:--------------------------------");
List scanList = getOSALastOSAStatus(projectId);
for (OSAScanStatus s : scanList) {
if (Status.SUCCEEDED.value().equals(s.getState().getName())) {
osaResults = retrieveOSAResults(s.getId(), s, projectId);
break;
}
}
} catch (Exception e) {
log.error(e.getMessage());
osaResults.setException(new CxClientException("Error getting last scan results.", e));
}
return osaResults;
}
//Private Methods
private String sendOSAScan(String osaDependenciesJson, long projectId) throws IOException {
log.info("Sending OSA scan request");
CreateOSAScanResponse osaScan = sendOSARequest(projectId, osaDependenciesJson);
//String summaryLink = OSAUtils.composeProjectOSASummaryLink(config.getUrl(), projectId);
log.info("OSA scan created successfully. Link to project state: " + config.getUrl() + LINK_FORMAT + projectId + LINK_FORMAL_SUMMARY);
return osaScan.getScanId();
}
private CreateOSAScanResponse sendOSARequest(long projectId, String osaDependenciesJson) throws IOException {
CreateOSAScanRequest req = new CreateOSAScanRequest(projectId, osaDependenciesJson);
StringEntity entity = new StringEntity(convertToJson(req), StandardCharsets.UTF_8);
return httpClient.postRequest(OSA_SCAN_PROJECT, CONTENT_TYPE_APPLICATION_JSON_V1, entity, CreateOSAScanResponse.class, 201, "create OSA scan");
}
private OSASummaryResults getOSAScanSummaryResults(String scanId) throws IOException {
String relativePath = OSA_SCAN_SUMMARY + SCAN_ID_QUERY_PARAM + scanId;
return httpClient.getRequest(relativePath, CONTENT_TYPE_APPLICATION_JSON_V1, OSASummaryResults.class, 200, "OSA scan summary results", false);
}
private List getOSALastOSAStatus(long projectId) throws IOException {
return (List) httpClient.getRequest(OSA_SCANS + PROJECT_ID_QUERY_PARAM + projectId, CONTENT_TYPE_APPLICATION_JSON_V1, OSAScanStatus.class, 200, " last OSA scan ID", true);
}
private List getOSALibraries(String scanId) throws IOException {
String relPath = OSA_SCAN_LIBRARIES + SCAN_ID_QUERY_PARAM + scanId + ITEM_PER_PAGE_QUERY_PARAM + MAX_ITEMS;
return (List) httpClient.getRequest(relPath, CONTENT_TYPE_APPLICATION_JSON_V1, Library.class, 200, "OSA libraries", true);
}
private List getOSAVulnerabilities(String scanId) throws IOException {
String relPath = OSA_SCAN_VULNERABILITIES + SCAN_ID_QUERY_PARAM + scanId + ITEM_PER_PAGE_QUERY_PARAM + MAX_ITEMS;
return (List) httpClient.getRequest(relPath, CONTENT_TYPE_APPLICATION_JSON_V1, CVE.class, 200, "OSA vulnerabilities", true);
}
//Waiter - overload methods
private OSAScanStatus getOSAScanStatus(String scanId) throws IOException {
String relPath = OSA_SCAN_STATUS.replace("{scanId}", scanId);
OSAScanStatus scanStatus = httpClient.getRequest(relPath, CONTENT_TYPE_APPLICATION_JSON_V1, OSAScanStatus.class, 200, "OSA scan status", false);
int stateId = scanStatus.getState().getId();
if (OSAScanStatusEnum.SUCCEEDED.getNum() == stateId) {
scanStatus.setBaseStatus(Status.SUCCEEDED);
} else if (OSAScanStatusEnum.IN_PROGRESS.getNum() == stateId || OSAScanStatusEnum.NOT_STARTED.getNum() == stateId) {
scanStatus.setBaseStatus(Status.IN_PROGRESS);
} else {
scanStatus.setBaseStatus(Status.FAILED);
}
return scanStatus;
}
private void printOSAProgress(OSAScanStatus scanStatus, long startTime) {
String timestamp = ShragaUtils.getTimestampSince(startTime);
log.info("Waiting for OSA scan results. Elapsed time: " + timestamp + ". " +
"Status: " + scanStatus.getState().getName());
}
private OSAScanStatus resolveOSAStatus(OSAScanStatus scanStatus) {
if (scanStatus == null) {
throw new CxClientException("OSA scan cannot be completed.");
} else if (Status.FAILED == scanStatus.getBaseStatus()) {
String failedMsg = scanStatus.getState() == null ? "" : "status [" + scanStatus.getState().getName() + "]. Reason: " + scanStatus.getState().getFailureReason();
throw new CxClientException("OSA scan cannot be completed. " + failedMsg);
}
if (Status.SUCCEEDED == scanStatus.getBaseStatus()) {
log.info("OSA scan finished.");
}
return scanStatus;
}
private void ensureProjectIdSpecified() {
if (projectId == 0) {
throw new CxClientException("projectId must be set before executing this method.");
}
}
}