Skip to content

Commit

Permalink
include-SCA support (#165)
Browse files Browse the repository at this point in the history
* new scan class for SAST + SCA
* add method to get noncompliant issues from scan
* include-SCA implementation

---------

Co-authored-by: Matthew <[email protected]>
  • Loading branch information
vishalhcl-5960 and mattmurp authored Jul 23, 2024
1 parent bbe161b commit 6127c89
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 97 deletions.
2 changes: 2 additions & 0 deletions src/main/java/com/hcl/appscan/sdk/CoreConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public interface CoreConstants {
String ITEMS = "Items"; //$NON-NLS-1$

String CREATE_SCAN_SUCCESS = "message.created.scan"; //$NON-NLS-1$
String SCAN_OVERVIEW = "message.scan.overview"; //$NON-NLS-1$
String DOWNLOADING_CLIENT = "message.downloading.client"; //$NON-NLS-1$
String EXECUTING_SCAN = "message.running.scan"; //$NON-NLS-1$
String UPLOADING_FILE = "message.uploading.file"; //$NON-NLS-1$
Expand All @@ -123,6 +124,7 @@ public interface CoreConstants {
String ERROR_AUTHENTICATING = "error.authenticating"; //$NON-NLS-1$
String ERROR_DOWNLOADING_CLIENT = "error.download.client"; //$NON-NLS-1$
String ERROR_GETTING_DETAILS = "error.getting.details"; //$NON-NLS-1$
String ERROR_GETTING_DETAILS_SCAN_ID = "error.getting.details.scan.id"; //$NON-NLS-1$
String ERROR_GETTING_RESULT = "error.getting.result"; //$NON-NLS-1$
String ERROR_GENERATING_REPORT = "error.generating.report"; //$NON-NLS-1$
String ERROR_INVALID_APP = "error.invalid.app"; //$NON-NLS-1$
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/com/hcl/appscan/sdk/messages.properties
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#
# \u00c2\u00a9 Copyright IBM Corporation 2016.
# \u00c2\u00a9 Copyright HCL Technologies Ltd. 2017, 2020, 2024.
# \u00c2\u00a9 Copyright HCL Technologies Ltd. 2017, 2024.
# LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0
#
# NLS_MESSAGEFORMAT_VAR
# NLS_ENCODING=UNICODE

transfer.progress={0}% transferred

message.created.scan=Successfully submitted scan for analysis.
message.created.scan=Successfully submitted {0} scan for analysis. Scan ID: {1}
message.scan.overview={0} scan overview: {1}
message.running.scan=Creating and executing {0} scan...
message.uploading.file=Uploading {0} to the analysis service...
message.done=Done.
Expand All @@ -25,8 +26,9 @@ message.suspend.job.byuser=Scan has been Suspended by User, {0}
error.authenticating=An error occurred authenticating with the service.
error.download.client=An error occurred downloading the SAClientUtil package. {0}
error.getting.details=An error occurred retrieving the scan details. {0}
error.getting.details.scan.id= An error occurred retrieving the scan details. Scan ID: {0}
error.getting.result=An error occurred retrieving the scan result. {0}
error.generating.report=An error occurred in generating the report. {0}
error.generating.report=An error occurred in generating the report. {0}
error.invalid.app=Application with id {0} does not exist.
error.invalid.job.id=Job ID {0} is invalid or does not exist.
error.loading.apps=An error occurred retrieving the application list. {0}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* © Copyright HCL Technologies Ltd. 2024.
* LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0
*/

package com.hcl.appscan.sdk.results;

import java.io.File;
import java.io.Serializable;
import java.util.Collection;

import com.hcl.appscan.sdk.CoreConstants;
import com.hcl.appscan.sdk.logging.IProgress;

public class CloudCombinedResultsProvider implements IResultsProvider, Serializable {

private static final long serialVersionUID = 1L;

private IResultsProvider m_resultsProvider1;
private IResultsProvider m_resultsProvider2;
private String m_reportFormat = DEFAULT_REPORT_FORMAT;

public CloudCombinedResultsProvider(IResultsProvider resultsProvider1, IResultsProvider resultsProvider2) {
m_resultsProvider1 = resultsProvider1;
m_resultsProvider2 = resultsProvider2;
}

@Override
public boolean hasResults() {
return m_resultsProvider1.hasResults() || m_resultsProvider2.hasResults();
}

@Override
public String getStatus() {
String combinedStatus = CoreConstants.RUNNING;
String status1 = m_resultsProvider1.getStatus();
String status2 = m_resultsProvider2.getStatus();

if(status1.equalsIgnoreCase(CoreConstants.FAILED) || status2.equalsIgnoreCase(CoreConstants.FAILED)) {
combinedStatus = CoreConstants.FAILED;
}
else if(status1.equalsIgnoreCase(CoreConstants.READY) && status2.equalsIgnoreCase(CoreConstants.READY)) {
combinedStatus = CoreConstants.READY;
}

return combinedStatus;
}

@Override
public Collection<?> getFindings() {
return null;
}

@Override
public int getFindingsCount() {
return m_resultsProvider1.getFindingsCount() + m_resultsProvider2.getFindingsCount();
}

@Override
public int getCriticalCount() {
return m_resultsProvider1.getCriticalCount() + m_resultsProvider2.getCriticalCount();
}

@Override
public int getHighCount() {
return m_resultsProvider1.getHighCount() + m_resultsProvider2.getHighCount();
}

@Override
public int getMediumCount() {
return m_resultsProvider1.getMediumCount() + m_resultsProvider2.getMediumCount();
}

@Override
public int getLowCount() {
return m_resultsProvider1.getLowCount() + m_resultsProvider2.getLowCount();
}

@Override
public int getInfoCount() {
return m_resultsProvider1.getInfoCount() + m_resultsProvider2.getInfoCount();
}

@Override
public String getType() {
return m_resultsProvider1.getType() + "_" + m_resultsProvider2.getType();
}

@Override
public void getResultsFile(File destination, String format) {
//Append the technology type to the end of the file name.
String name = destination.getName();
File directory = destination.getParentFile();
m_resultsProvider1.getResultsFile(new File(directory, name), format);
m_resultsProvider2.getResultsFile(new File(directory, name), format);
}

@Override
public String getResultsFormat() {
return m_reportFormat;
}

@Override
public String getMessage() {
if(m_resultsProvider1.getMessage() != null && m_resultsProvider2.getMessage() != null) {
return m_resultsProvider1.getType() + ": " + m_resultsProvider1.getMessage() + "\n" +
m_resultsProvider2.getType() + ": " + m_resultsProvider2.getMessage();
}
return null;
}

@Override
public void setReportFormat(String format) {
m_reportFormat = format;
m_resultsProvider1.setReportFormat(format);
m_resultsProvider2.setReportFormat(format);
}

@Override
public void setProgress(IProgress progress) {
m_resultsProvider1.setProgress(progress);
m_resultsProvider2.setProgress(progress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
public class CloudResultsProvider implements IResultsProvider, Serializable, CoreConstants {

private static final long serialVersionUID = 1L;

private static String DEFAULT_REPORT_FORMAT = "html"; //$NON-NLS-1$

protected String m_type;
protected String m_scanId;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* © Copyright IBM Corporation 2016.
* © Copyright HCL Technologies Ltd. 2017, 2020.
* © Copyright HCL Technologies Ltd. 2017, 2024.
* LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0
*/

Expand All @@ -16,6 +16,8 @@
*/
public interface IResultsProvider {

public static String DEFAULT_REPORT_FORMAT = "html"; //$NON-NLS-1$

/**
* Answers whether or not this provider contains any results.
* @return True if the provider has results.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,67 +35,71 @@ public NonCompliantIssuesResultProvider(String scanId, String type, IScanService

@Override
protected void loadResults() {
try {
JSONObject items = m_scanProvider.getScanDetails(m_scanId);
JSONObject obj = items == null ? null : items.getJSONObject(LATEST_EXECUTION);
if (obj == null) {
m_status = FAILED;
return;
} else if (items.has(KEY) && items.get(KEY).equals(UNAUTHORIZED_ACTION)) {
m_status = FAILED;
return;
} else if (obj.has(STATUS) && obj.get(STATUS).equals(UNKNOWN)) {
m_status = UNKNOWN;
return;
}


m_status = obj.getString(STATUS);
if (FAILED.equalsIgnoreCase(m_status) && obj.has(USER_MESSAGE)) {
m_progress.setStatus(new Message(Message.ERROR, obj.getString(USER_MESSAGE)));
m_message = obj.getString(USER_MESSAGE);
} else if (PAUSED.equalsIgnoreCase(m_status)) {
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(SUSPEND_JOB_BYUSER, "Scan Id: " + m_scanId)));
m_message = Messages.getMessage(SUSPEND_JOB_BYUSER, "Scan Id: " + m_scanId);
} else if (m_status != null && !(m_status.equalsIgnoreCase(INQUEUE) || m_status.equalsIgnoreCase(RUNNING) || m_status.equalsIgnoreCase(PAUSING))) {
JSONArray array = m_scanProvider.getNonCompliantIssues(m_scanId);
m_totalFindings = 0;

for (int i = 0; i < array.length(); i++) {
JSONObject jobj = array.getJSONObject(i);
String sev = jobj.getString("Severity");
int count = jobj.getInt("N");

switch (sev.toLowerCase()) {
case "critical":
m_criticalFindings += count;
m_totalFindings += count;
break;
case "high":
m_highFindings += count;
m_totalFindings += count;
break;
case "medium":
m_mediumFindings += count;
m_totalFindings += count;
break;
case "low":
m_lowFindings += count;
m_totalFindings += count;
break;
case "informational":
m_infoFindings += count;
m_totalFindings += count;
break;
default:
m_totalFindings += count;
break;
}
}
setHasResult(true);
m_message = "";
} else if (RUNNING.equalsIgnoreCase(m_status)) m_message = "";
} catch (IOException | JSONException | NullPointerException e) {
try {
if (m_scanId == null) {
m_status = FAILED;
} else {
JSONObject items = m_scanProvider.getScanDetails(m_scanId);
JSONObject obj = items == null ? null : items.getJSONObject(LATEST_EXECUTION);
if (obj == null) {
m_status = FAILED;
return;
} else if (items.has(KEY) && items.get(KEY).equals(UNAUTHORIZED_ACTION)) {
m_status = FAILED;
return;
} else if (obj.has(STATUS) && obj.get(STATUS).equals(UNKNOWN)) {
m_status = UNKNOWN;
return;
}


m_status = obj.getString(STATUS);
if (FAILED.equalsIgnoreCase(m_status) && obj.has(USER_MESSAGE)) {
m_progress.setStatus(new Message(Message.ERROR, obj.getString(USER_MESSAGE)));
m_message = obj.getString(USER_MESSAGE);
} else if (PAUSED.equalsIgnoreCase(m_status)) {
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(SUSPEND_JOB_BYUSER, "Scan Id: " + m_scanId)));
m_message = Messages.getMessage(SUSPEND_JOB_BYUSER, "Scan Id: " + m_scanId);
} else if (m_status != null && !(m_status.equalsIgnoreCase(INQUEUE) || m_status.equalsIgnoreCase(RUNNING) || m_status.equalsIgnoreCase(PAUSING))) {
JSONArray array = m_scanProvider.getNonCompliantIssues(m_scanId);
m_totalFindings = 0;

for (int i = 0; i < array.length(); i++) {
JSONObject jobj = array.getJSONObject(i);
String sev = jobj.getString("Severity");
int count = jobj.getInt("N");

switch (sev.toLowerCase()) {
case "critical":
m_criticalFindings += count;
m_totalFindings += count;
break;
case "high":
m_highFindings += count;
m_totalFindings += count;
break;
case "medium":
m_mediumFindings += count;
m_totalFindings += count;
break;
case "low":
m_lowFindings += count;
m_totalFindings += count;
break;
case "informational":
m_infoFindings += count;
m_totalFindings += count;
break;
default:
m_totalFindings += count;
break;
}
}
setHasResult(true);
m_message = "";
} else if (RUNNING.equalsIgnoreCase(m_status)) m_message = "";
}
} catch (IOException | JSONException | NullPointerException e) {
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS, e.getMessage())),
e);
m_status = FAILED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.hcl.appscan.sdk.scanners.dynamic.DASTConstants;
import com.hcl.appscan.sdk.utils.FileUtil;
import com.hcl.appscan.sdk.utils.ServiceUtil;
import org.apache.wink.json4j.JSONArray;
import org.apache.wink.json4j.JSONArtifact;
import org.apache.wink.json4j.JSONException;
Expand Down Expand Up @@ -59,28 +60,21 @@ public String createAndExecuteScan(String type, Map<String, String> params) {
HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts());

try {
HttpResponse response;
request_headers.put("Content-Type", "application/json");
request_headers.put("accept", "application/json");
String request_url;

if(type.equals(SASTConstants.STATIC_ANALYZER) && !params.containsKey(UPLOAD_DIRECT) && params.containsKey(OPEN_SOURCE_ONLY)) {
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(EXECUTING_SCAN, CoreConstants.SOFTWARE_COMPOSITION_ANALYZER)));
request_url = m_authProvider.getServer() + String.format(API_SCANNER, SCA);
} else {
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(EXECUTING_SCAN, params.get(CoreConstants.SCANNER_TYPE))));
request_url = m_authProvider.getServer() + String.format(API_SCANNER, type);
}

response = client.post(request_url,request_headers,params);

String request_url = m_authProvider.getServer() + String.format(API_SCANNER, type);

HttpResponse response = client.post(request_url,request_headers,params);
int status = response.getResponseCode();

JSONObject json = (JSONObject) response.getResponseBodyAsJSON();

if (status == HttpsURLConnection.HTTP_CREATED || status == HttpsURLConnection.HTTP_OK) {
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATE_SCAN_SUCCESS)));
return json.getString(ID);
String scanId = json.getString(ID);
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATE_SCAN_SUCCESS, type.toUpperCase(), scanId)));
String scanOverviewUrl = m_authProvider.getServer() + "/main/myapps/" + params.get(CoreConstants.APP_ID) + "/scans/" + scanId;
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(SCAN_OVERVIEW, type.toUpperCase(), scanOverviewUrl)));
return scanId;
} else if (json != null && json.has(MESSAGE)) {
String errorResponse = json.getString(MESSAGE);
if(json.has(FORMAT_PARAMS) && !json.isNull(FORMAT_PARAMS)) {
Expand Down Expand Up @@ -151,12 +145,12 @@ public JSONObject getScanDetails(String scanId) throws IOException, JSONExceptio
JSONObject obj = (JSONObject) response.getResponseBodyAsJSON();
JSONArray array = (JSONArray) obj.get(ITEMS);
if(array.isEmpty()) {
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS, scanId)));
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS_SCAN_ID, scanId)));
} else {
return (JSONObject) array.getJSONObject(0);
}
} else if (response.getResponseCode() == -1) {
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS, scanId)));
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS_SCAN_ID, scanId)));
} else if (response.getResponseCode() != HttpsURLConnection.HTTP_BAD_REQUEST) {
JSONArtifact json = response.getResponseBodyAsJSON();
if (json != null && ((JSONObject)json).has(MESSAGE))
Expand Down
Loading

0 comments on commit 6127c89

Please sign in to comment.