com.cx.restclient.sast.utils.LegacyClient 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.sast.utils;
import com.cx.restclient.common.UrlUtils;
import com.cx.restclient.configuration.CxScanConfig;
import com.cx.restclient.cxArm.dto.CxArmConfig;
import com.cx.restclient.dto.*;
import com.cx.restclient.exception.CxClientException;
import com.cx.restclient.exception.CxHTTPClientException;
import com.cx.restclient.httpClient.CxHttpClient;
import com.cx.restclient.osa.dto.ClientType;
import com.cx.restclient.sast.dto.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpResponseException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.json.JSONException;
import org.slf4j.Logger;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static com.cx.restclient.common.CxPARAM.*;
import static com.cx.restclient.httpClient.utils.ContentType.CONTENT_TYPE_APPLICATION_JSON_V1;
import static com.cx.restclient.httpClient.utils.ContentType.CONTENT_TYPE_APPLICATION_JSON_V4;
import static com.cx.restclient.httpClient.utils.HttpClientHelper.convertToJson;
import static com.cx.restclient.sast.utils.SASTParam.*;
/**
* Common parent for SAST and OSA clients.
* Extracted from {@link com.cx.restclient.CxClientDelegator} for better maintainability.
*/
public abstract class LegacyClient {
private static final String DEFAULT_AUTH_API_PATH = "CxRestApi/auth/" + AUTHENTICATION;
public static final String PRESETNAME_PROJET_SETTING_DEFAULT = "Project Default";
public static final String PRESETID_PROJET_SETTING_DEFAULT = "0";
private static final Integer UNKNOWN_INT = -1;
private static final String ID_PATH_PARAM = "{id}";
protected CxHttpClient httpClient;
protected CxScanConfig config;
protected Logger log;
private String teamPath;
protected long projectId;
private State state = State.SUCCESS;
private boolean isNewProject = false;
public LegacyClient(CxScanConfig config, Logger log) throws MalformedURLException {
this.config = config;
this.log = log;
initHttpClient(config, log);
validateConfig(config);
}
public void setConfig(CxScanConfig config) {
this.config = config;
}
public void close() {
if (httpClient != null) {
httpClient.close();
}
}
public boolean isIsNewProject() {
return isNewProject;
}
public void setIsNewProject(boolean isNewProject) {
this.isNewProject = isNewProject;
}
public long resolveProjectId() throws IOException {
List projects = getProjectByName(config.getProjectName(), config.getTeamId(), teamPath);
if (projects == null || projects.isEmpty()) { // Project is new
if (config.getDenyProject()) {
throw new CxClientException(DENY_NEW_PROJECT_ERROR.replace("{projectName}", config.getProjectName()));
}
//Create newProject and checking if EnableSASTBranching is enabled then creating branch project
if(config.isEnableSASTBranching()) {
if (StringUtils.isEmpty(config.getMasterBranchProjName())) {
throw new CxClientException("Master branch project name is must to create branched project.");
} else {
Long masterProjectId;
List masterProject = getProjectByName(config.getMasterBranchProjName(), config.getTeamId(),
teamPath);
if (masterProject != null && !masterProject.isEmpty()) {
masterProjectId = masterProject.get(0).getId();
} else {
throw new CxClientException(
"Master branch project does not exist:" + config.getMasterBranchProjName());
}
log.info("Project not found, creating a new one.: '{}' with Team '{}'", config.getProjectName(),
teamPath);
projectId = createChildProject(masterProjectId, config.getProjectName());
if (projectId == UNKNOWN_INT) {
throw new CxClientException(
"Branched project could not be created: " + config.getProjectName());
}else {
checkCreateBranchProjectStatus(projectId);
log.info("Created a project with ID {}", projectId);
if(config.isEnableDataRetention()){
setRetentionRate(projectId);
}
setIsNewProject(true);
}
}
}
else {
CreateProjectRequest request = new CreateProjectRequest(config.getProjectName(), config.getTeamId(),
config.getPublic());
log.info("Project not found, creating a new one.: '{}' with Team '{}'", config.getProjectName(),
teamPath);
projectId = createNewProject(request, teamPath).getId();
log.info("Created a project with ID {}", projectId);
if(config.isEnableDataRetention()) {
setRetentionRate(projectId);
}
setIsNewProject(true);
}
} else {
projectId = projects.get(0).getId();
if(config.isEnableDataRetention()) {
setRetentionRate(projectId);
}
setIsNewProject(false);
log.info("Project already exists with ID {}", projectId);
}
return projectId;
}
private void checkCreateBranchProjectStatus(long branchprojectId) throws IOException {
// TODO Auto-generated method stub
String Status = "";
CreateBranchStatus getBranchRequest = null;
int timeout = checkTimeOut();
for (int i = 0; i < 3; i++) {
try {
getBranchRequest = populateBranchStatusList(branchprojectId);
} catch (Exception e) {
log.info("Version is less than SAST 9.5 V4");
break;
}
if (getBranchRequest != null) {
Status = getBranchRequest.getStatus().getValue();
log.info("Interval =" + i + " BranchStatus=" + Status);
if (Status.equals("Completed")) {
break;
} else {
waitTime(timeout);
}
}
}
}
private void waitTime(int timeout) {
try {
log.info("timeout =" + timeout +" Seconds");
Thread.sleep(timeout*1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
private int checkTimeOut() {
int timeout = 10;
if((config.getcopyBranchTimeOutInSeconds())!=null) {
timeout = config.getcopyBranchTimeOutInSeconds();
log.info("copybranchtimeoutinseconds =" + timeout +" Seconds");
}
if (timeout > 0 && timeout < 60 ) {
log.info("copybranchtimeoutinseconds is "+ timeout +"seconds");
} else {
timeout = 10;
log.warn("copybranchtimeoutinseconds is not between the range of 0 to 60 seconds, using default timeout i.e. 10 seconds");
}
return timeout;
}
private CreateBranchStatus populateBranchStatusList(long branchprojectId) throws IOException, CxClientException {
return httpClient.getRequest(PROJECT_BRANCH_ID.replace("{id}", Long.toString(branchprojectId)), CONTENT_TYPE_APPLICATION_JSON_V4, CreateBranchStatus.class, 200, "branch status", false);
}
public String configureTeamPath() throws IOException, CxClientException {
if (StringUtils.isEmpty(config.getTeamPath())) {
List teamList = populateTeamList();
// If there is no chosen teamPath, just add first one from the teams
// list as default
if (StringUtils.isEmpty(teamPath) && teamList != null && !teamList.isEmpty()) {
teamPath = teamList.get(0).getFullName();
}
} else {
teamPath = config.getTeamPath();
}
httpClient.setTeamPathHeader(teamPath);
log.debug(String.format("setTeamPathHeader %s", teamPath));
return teamPath;
}
public List getTeamList() throws IOException, CxClientException {
return populateTeamList();
}
private List populateTeamList() throws IOException {
return (List) httpClient.getRequest(CXTEAMS, CONTENT_TYPE_APPLICATION_JSON_V1, Team.class, 200, "team list", true);
}
public String getToken() throws IOException, CxClientException {
LoginSettings settings = getDefaultLoginSettings();
settings.setClientTypeForPasswordAuth(ClientType.CLI);
final TokenLoginResponse tokenLoginResponse = getHttpClient().generateToken(settings);
return tokenLoginResponse.getRefresh_token();
}
public void revokeToken(String token) throws IOException, CxClientException {
getHttpClient().revokeToken(token);
}
private Project createNewProject(CreateProjectRequest request, String teamPath) throws IOException {
String json = convertToJson(request);
httpClient.setTeamPathHeader(teamPath);
StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
return httpClient.postRequest(CREATE_PROJECT, CONTENT_TYPE_APPLICATION_JSON_V1, entity, Project.class, 201, "create new project: " + request.getName());
}
private List getProjectByName(String projectName, String teamId, String teamPath) throws IOException, CxClientException {
projectName = URLEncoder.encode(projectName, "UTF-8");
String projectNamePath = SAST_GET_PROJECT.replace("{name}", projectName).replace("{teamId}", teamId);
List projects = null;
try {
httpClient.setTeamPathHeader(teamPath);
projects = (List) httpClient.getRequest(projectNamePath, CONTENT_TYPE_APPLICATION_JSON_V1, Project.class, 200, "project by name: " + projectName, true);
} catch (CxHTTPClientException ex) {
if (ex.getStatusCode() != 404) {
throw ex;
}
}
return projects;
}
private void initHttpClient(CxScanConfig config, Logger log) throws MalformedURLException {
if (!org.apache.commons.lang3.StringUtils.isEmpty(config.getUrl())) {
httpClient = new CxHttpClient(
UrlUtils.parseURLToString(config.getUrl(), "CxRestAPI/"),
config.getCxOrigin(),
config.getCxOriginUrl(),
config.isDisableCertificateValidation(),
config.isUseSSOLogin(),
config.getRefreshToken(),
config.isProxy(),
config.getProxyConfig(),
log,
config.getNTLM());
}
}
public void initiate() throws CxClientException {
try {
if (config.isSastOrOSAEnabled()) {
String version = getCxVersion();
login(version);
resolveTeam();
//httpClient.setTeamPathHeader(this.teamPath);
if (config.isSastEnabled()) {
resolvePreset();
}
if (config.getEnablePolicyViolations() || config.getEnablePolicyViolationsSCA()) {
resolveCxARMUrl();
}
resolveEngineConfiguration();
resolveProjectId();
resolvePostScanAction();
}
} catch (Exception e) {
throw new CxClientException(e);
}
}
public String getCxVersion() throws IOException, CxClientException {
String version;
try {
config.setCxVersion(httpClient.getRequest(CX_VERSION, CONTENT_TYPE_APPLICATION_JSON_V1, CxVersion.class, 200, "cx Version", false));
String hotfix = "";
try {
if (config.getCxVersion().getHotFix() != null && Integer.parseInt(config.getCxVersion().getHotFix()) > 0) {
hotfix = " Hotfix [" + config.getCxVersion().getHotFix() + "].";
}
} catch (Exception ex) {
}
version = config.getCxVersion().getVersion();
log.info("Checkmarx server version [" + config.getCxVersion().getVersion() + "]." + hotfix);
} catch (Exception ex) {
version = "lower than 9.0";
log.debug("Checkmarx server version [lower than 9.0]");
}
return version;
}
public String login(Boolean isVersionRequired) throws IOException {
String cxVersion = getCxVersion();
login(cxVersion);
return cxVersion;
}
public void login() throws IOException {
String version = getCxVersion();
login(version);
}
public void login(String version) throws IOException, CxClientException {
// perform login to server
log.info("Logging into the Checkmarx service.");
if (config.getToken() != null) {
httpClient.setToken(config.getToken());
return;
}
LoginSettings settings = getDefaultLoginSettings();
settings.setRefreshToken(config.getRefreshToken());
settings.setVersion(version);
httpClient.login(settings);
}
public LoginSettings getDefaultLoginSettings() throws MalformedURLException {
String baseUrl = UrlUtils.parseURLToString(config.getUrl(), DEFAULT_AUTH_API_PATH);
LoginSettings result = LoginSettings.builder()
.accessControlBaseUrl(baseUrl)
.username(config.getUsername())
.password(config.getPassword())
.clientTypeForPasswordAuth(ClientType.RESOURCE_OWNER)
.clientTypeForRefreshToken(ClientType.CLI)
.build();
result.getSessionCookies().addAll(config.getSessionCookie());
return result;
}
public CxHttpClient getHttpClient() {
return httpClient;
}
private void resolveEngineConfiguration() throws IOException {
if (config.getEngineConfigurationId() == null && config.getEngineConfigurationName() == null) {
config.setEngineConfigurationId(1);
} else if (config.getEngineConfigurationName() != null) {
final List engineConfigurations = getEngineConfiguration();
for (EngineConfiguration engineConfiguration : engineConfigurations) {
if (engineConfiguration.getName().equalsIgnoreCase(config.getEngineConfigurationName())) {
config.setEngineConfigurationId(engineConfiguration.getId());
log.info(String.format("Engine configuration: \"%s\" was validated in server", config.getEngineConfigurationName()));
}else{
if ("Improved Scan Flow".equalsIgnoreCase(config.getEngineConfigurationName())){
config.setEngineConfigurationId(1);
}
}
}
if (config.getEngineConfigurationId() == null) {
throw new CxClientException("Engine configuration: \"" + config.getEngineConfigurationName() + "\" was not found in server");
}
}
}
public List getEngineConfiguration() throws IOException {
this.teamPath = configureTeamPath();
httpClient.setTeamPathHeader(this.teamPath);
return (List) httpClient.getRequest(SAST_ENGINE_CONFIG, CONTENT_TYPE_APPLICATION_JSON_V1, EngineConfiguration.class, 200, "engine configurations", true);
}
public void validateConfig(CxScanConfig config) throws CxClientException {
String message = null;
if (config == null) {
message = "Non-null config must be provided.";
} else if (org.apache.commons.lang3.StringUtils.isEmpty(config.getUrl()) && config.isSastOrOSAEnabled()) {
message = "Server URL is required when SAST or OSA is enabled.";
}
if (message != null) {
throw new CxClientException(message);
}
}
private void resolveTeam() throws CxClientException, IOException {
config.setTeamPath(configureTeamPath());
if (config.getTeamId() == null) {
config.setTeamId(getTeamIdByName(config.getTeamPath()));
}
printTeamPath();
}
public String getTeamIdByName(String teamName) throws CxClientException, IOException {
teamName = replaceDelimiters(teamName);
List allTeams = getTeamList();
for (Team team : allTeams) {
String fullName = replaceDelimiters(team.getFullName());
if (fullName.equalsIgnoreCase(teamName)) { //TODO caseSenesitive
return team.getId();
}
}
throw new CxClientException("Could not resolve team ID from team name: " + teamName);
}
private String replaceDelimiters(String teamName) {
while (teamName.contains("\\") || teamName.contains("//")) {
teamName = teamName.replace("\\", "/");
teamName = teamName.replace("//", "/");
}
return teamName;
}
private CxArmConfig getCxARMConfig() throws IOException, CxClientException {
httpClient.setTeamPathHeader(this.teamPath);
return httpClient.getRequest(CX_ARM_URL, CONTENT_TYPE_APPLICATION_JSON_V1, CxArmConfig.class, 200, "CxARM URL", false);
}
private void resolveCxARMUrl() throws CxClientException {
try {
this.config.setCxARMUrl(getCxARMConfig().getCxARMPolicyURL());
} catch (Exception ex) {
throw new CxClientException("CxARM is not available. Policy violations cannot be calculated: " + ex.getMessage());
}
}
//Some plugins share preset name while others share presetId in CxScanConfig
//If is given preference.
//presetId=0 is a special case, which does not exist in SAST but SAST has a special meaning for it,
// which is to use whatever preset available on the project settings.
private void resolvePreset() throws CxClientException, IOException {
if (config.getPresetId() == null && !StringUtils.isEmpty(config.getPresetName())) {
if(PRESETNAME_PROJET_SETTING_DEFAULT.equalsIgnoreCase(config.getPresetName()))
config.setPresetId(Integer.parseInt(PRESETID_PROJET_SETTING_DEFAULT));
else
config.setPresetId(getPresetIdByName(config.getPresetName()));
}else if(config.getPresetId() == Integer.parseInt(PRESETID_PROJET_SETTING_DEFAULT)) {
config.setPresetName(PRESETNAME_PROJET_SETTING_DEFAULT);
}else {
config.setPresetName(getPresetById(config.getPresetId()).getName());
}
log.info(String.format("preset name: %s preset id: %s", config.getPresetName(), config.getPresetId()));
}
private void resolvePostScanAction() throws CxClientException, IOException {
if (config.getPostScanActionId() == null && !StringUtils.isEmpty(config.getPostScanName())) {
config.setPostScanActionId(getPostScanActionIdByName(config.getPostScanName()));
log.info(String.format("post scan action name: %s post scan action id: %s", config.getPostScanName(),
config.getPostScanActionId()));
}
}
public int getPresetIdByName(String presetName) throws CxClientException, IOException {
List allPresets = getPresetList();
for (Preset preset : allPresets) {
if (preset.getName().equalsIgnoreCase(presetName)) { //TODO caseSenesitive- checkkk
return preset.getId();
}
}
throw new CxClientException("Could not resolve preset ID from preset name: " + presetName);
}
public List getPresetList() throws IOException, CxClientException {
configureTeamPath();
return (List) httpClient.getRequest(CXPRESETS, CONTENT_TYPE_APPLICATION_JSON_V1, Preset.class, 200, "preset list", true);
}
public Preset getPresetById(int presetId) throws IOException, CxClientException {
httpClient.setTeamPathHeader(this.teamPath);
return httpClient.getRequest(CXPRESETS + "/" + presetId, CONTENT_TYPE_APPLICATION_JSON_V1, Preset.class, 200, "preset by id", false);
}
public List getPostScanActionList() throws IOException, CxClientException {
configureTeamPath();
return (List) httpClient.getRequest(SAST_CUSTOM_TASKS, CONTENT_TYPE_APPLICATION_JSON_V1, PostAction.class, 200, "post scan action list", true);
}
public int getPostScanActionIdByName(String name) throws CxClientException, IOException {
List allPostActionItems = getPostScanActionList();
for (PostAction postAction : allPostActionItems) {
if (postAction.getName().equalsIgnoreCase(name)) { // TODO caseSenesitive- checkkk
return postAction.getId();
}
}
throw new CxClientException("Could not resolve post scan item ID from post scan action list: " + name);
}
private void printTeamPath() {
try {
this.teamPath = config.getTeamPath();
if (this.teamPath == null) {
this.teamPath = getTeamNameById(config.getTeamId());
}
log.info(String.format("full team path: %s", this.teamPath));
} catch (Exception e) {
log.warn("Error getting team path.");
}
}
private void setRetentionRate(long projectId) throws IOException {
DataRetentionSettingsDto retentionRequest = new DataRetentionSettingsDto(config.getProjectRetentionRate());
log.info("Sending request to set retentionRate for project with id {}",projectId);
String json = convertToJson(retentionRequest);
httpClient.setTeamPathHeader(teamPath);
HttpEntity entity = new StringEntity(json);
try{
httpClient.postRequest(SAST_RETENTION_RATE.replace(ID_PATH_PARAM, Long.toString(projectId)), CONTENT_TYPE_APPLICATION_JSON_V1, entity, CxID.class, 204, "Set retention Rate for project");
log.info("Set '{}' Retention Rate for project with project ID : '{}' ",config.getProjectRetentionRate(), projectId);
}catch (CxHTTPClientException exception){
log.info(exception.getMessage());
log.info("Fail to set Retention Rate for project with project ID {}", projectId);
}
}
public String getTeamNameById(String teamId) throws CxClientException, IOException {
List allTeams = getTeamList();
for (Team team : allTeams) {
if (teamId.equals(team.getId())) {
return team.getFullName();
}
}
throw new CxClientException("Could not resolve team name from id: " + teamId);
}
public List getAllProjects() throws IOException, CxClientException {
List projects = null;
configureTeamPath();
try {
projects = (List) httpClient.getRequest(SAST_GET_ALL_PROJECTS, CONTENT_TYPE_APPLICATION_JSON_V1, Project.class, 200, "all projects", true);
} catch (HttpResponseException ex) {
if (ex.getStatusCode() != 404) {
throw ex;
}
}
return projects;
}
public Project getProjectById(String projectId, String contentType) throws IOException, CxClientException {
String projectNamePath = SAST_GET_PROJECT_BY_ID.replace("{projectId}", projectId);
Project projects = null;
try {
httpClient.setTeamPathHeader(this.teamPath);
projects = httpClient.getRequest(projectNamePath, contentType, Project.class, 200, "project by id: " + projectId, false);
} catch (CxHTTPClientException ex) {
if (ex.getStatusCode() != 404) {
throw ex;
}
}
return projects;
}
public List getConfigurationSetList() throws IOException, CxClientException {
configureTeamPath();
return (List) httpClient.getRequest(SAST_ENGINE_CONFIG, CONTENT_TYPE_APPLICATION_JSON_V1, CxNameObj.class, 200, "engine configurations", true);
}
public String getTeamPath() {
return teamPath;
}
public void setTeamPath(String teamPath) {
this.teamPath = teamPath;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
///function to create child project from master branch
public long createChildProject(long projectId, String childProjectName)throws IOException, CxClientException {
long childProjectId = UNKNOWN_INT;
CreateProjectRequest request = new CreateProjectRequest(childProjectName);
String json = convertToJson(request);
httpClient.setTeamPathHeader(teamPath);
StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
log.info("Creating branched project with name '{}' from existing project with ID {}", childProjectName, projectId);
try {
Project obj = httpClient.postRequest(PROJECT_BRANCH.replace("{id}", Long.toString(projectId)), CONTENT_TYPE_APPLICATION_JSON_V1, entity, Project.class, 201, "branch project");
if (obj != null) {
childProjectId = obj.getId();
return childProjectId;
} else {
log.error("CX Response for branch project request with name '{}' from existing project with ID {} was null", childProjectName, projectId);
}
}
catch (CxHTTPClientException e) {
log.error(e.getMessage());
log.error("Error occured while creating branched project with name '{}' from existing project with ID {}", childProjectName, projectId);
}
catch (JSONException e) {
log.error("Error processing JSON Response while creating branched project with name '{}' from existing project with ID {}", childProjectName, projectId);
log.error(ExceptionUtils.getStackTrace(e));
}
return childProjectId;
}
}