Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCA Support #116

3 changes: 3 additions & 0 deletions src/main/java/com/hcl/appscan/sdk/CoreConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public interface CoreConstants {
String CLIENT_TYPE = "ClientType"; //$NON-NLS-1$

String API_ENV = "/api/v2"; //$NON-NLS-1$
String API_ENV_V4 = "/api/v4"; //$NON-NLS-1$
String API_BLUEMIX = "Bluemix"; //$NON-NLS-1$
String API_BLUEMIX_LOGIN = API_ENV + "/Account/BluemixLogin"; //$NON-NLS-1$
String API_KEY_LOGIN = API_ENV + "/Account/ApiKeyLogin"; //$NON-NLS-1$
Expand All @@ -63,6 +64,7 @@ public interface CoreConstants {
String API_FILE_UPLOAD = API_ENV + "/FileUpload"; //$NON-NLS-1$
String API_SCAN = API_ENV + "/%s"; //$NON-NLS-1$
String API_SCANNER = API_ENV + "/Scans/%s"; //$NON-NLS-1$
String API_SCANNER_V4 = API_ENV_V4 + "/Scans/%s"; //$NON-NLS-1$
String API_SCANS = API_ENV + "/Scans"; //$NON-NLS-1$
String API_NONCOMPLIANT_ISSUES = API_ENV + "/Scans/%s/NonCompliantIssues"; //$NON-NLS-1$
String API_SCANS_REPORT = API_ENV + "/Scans/%s/Report/%s"; //$NON-NLS-1$
Expand Down Expand Up @@ -127,6 +129,7 @@ public interface CoreConstants {
String ERROR_UPLOADING_FILE = "error.upload.file"; //$NON-NLS-1$
String ERROR_GETTING_INFO = "error.getting.info"; //$NON-NLS-1$
String ERROR_URL_VALIDATION = "error.url.validation"; //$NON-NLS-1$
String WARNING_SCA = "warning.sca"; //$NON-NLS-1$
String FORMAT_PARAMS = "FormatParams"; //$NON-NLS-1$

// ASE Status Messages
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/com/hcl/appscan/sdk/http/HttpClient.java
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create a new class of HttpClient to accomodate the changes required for the support of SCA?

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

package com.hcl.appscan.sdk.http;

import org.apache.wink.json4j.JSON;
import org.apache.wink.json4j.JSONObject;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
Expand All @@ -16,6 +19,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -167,6 +171,39 @@ public HttpResponse postForm(String url,
return post(url, headerProperties, body);
}

/**
* Submit a form with parameters using the post request, mainly for v4 APIs.
*
* @param url The URL string.
* @param headerProperties An optional Map of header properties.
* @param params An optional Map of parameters.
* @return The response as a byte array.
* @throws IOException If an error occurs.
*/
public HttpResponse postFormV4(String url, Map<String, String> headerProperties, Map<String, String> params)
throws IOException {
Map<String, Object> objectMap = new HashMap<>();
for (String key : params.keySet()) {
String value = params.get(key);
if (value != null) {
if (value.equalsIgnoreCase("true")) {
objectMap.put(key, true);
} else if (value.equalsIgnoreCase("false")) {
objectMap.put(key, false);
} else {
// If the string is not "true" or "false," keep it as is
objectMap.put(key, value);
}
} else {
// If the value is not a string, keep it as is
objectMap.put(key, value);
}
}
JSONObject json = new JSONObject(objectMap);
String body = json.toString();
return post(url, headerProperties, body);
}

/**
* Submit a form with parameters using the put request.
*
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/hcl/appscan/sdk/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ error.http=Response Code: {0}\nReason: {1}
error.login.type.deprectated=The specified login type is deprecated. Please use API key and secret.
error.getting.info=An error occurred getting information for {0} with id {1}.
error.url.validation = An error occurred while validating the URL.
warning.sca = Note: To scan open-source files, use the Software Composition Analysis (SCA) scan type. AppScan on Cloud is phasing out open source-only scanning with static analysis scans.

#Presence
error.getting.presence.details=An error occurred retrieving details for Presence with id {0}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,34 @@ public String createAndExecuteScan(String type, Map<String, String> params) {
return null;

m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(EXECUTING_SCAN)));

String request_url = m_authProvider.getServer() + String.format(API_SCANNER, type);
Map<String, String> request_headers = m_authProvider.getAuthorizationHeader(true);

Map<String, String> request_headers = m_authProvider.getAuthorizationHeader(true);
String request_url;
if(type.equals("Sca")) {
// To execute the SCA scan we are using the V4 APIs.
request_url = m_authProvider.getServer() + String.format(API_SCANNER_V4, "Sca");
request_headers.put("Content-Type", "application/json");
request_headers.put("accept", "application/json");
} else {
request_url = m_authProvider.getServer() + String.format(API_SCANNER, type);
}

HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts());

try {
HttpResponse response = client.postForm(request_url, request_headers, params);
HttpResponse response;
if (type.equals("Sca")) {
response = client.postFormV4(request_url,request_headers,params);
} else {
response = client.postForm(request_url, request_headers, params);
}
int status = response.getResponseCode();

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

if (status == HttpsURLConnection.HTTP_CREATED) {
if (status == HttpsURLConnection.HTTP_CREATED || status == 200) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont we have a constant for 200 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we have, making the change.

m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATE_SCAN_SUCCESS)));
return json.getString(ID);
}
else if (json != null && json.has(MESSAGE)) {
} else if (json != null && json.has(MESSAGE)) {
String errorResponse = json.getString(MESSAGE);
if(json.has(FORMAT_PARAMS)) {
JSONArray jsonArray = json.getJSONArray(FORMAT_PARAMS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ private List<String> getClientArgs(Map<String, String> properties) {
args.add(OPT_VERBOSE);
if(properties.containsKey(THIRD_PARTY) || System.getProperty(THIRD_PARTY) != null)
args.add(OPT_THIRD_PARTY);
if (properties.containsKey(OPEN_SOURCE_ONLY) || System.getProperty(OPEN_SOURCE_ONLY) != null)
if (properties.containsKey(OPEN_SOURCE_ONLY) || System.getProperty(OPEN_SOURCE_ONLY) != null || properties.get(CoreConstants.SCANNER_TYPE).equals("Sca"))
args.add(OPT_OPEN_SOURCE_ONLY);
if (properties.containsKey(SOURCE_CODE_ONLY) || System.getProperty(SOURCE_CODE_ONLY) != null)
args.add(OPT_SOURCE_CODE_ONLY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public interface SASTConstants {
String APPSCAN_CLIENT_VERSION = "APPSCAN_CLIENT_VERSION"; //$NON-NLS-1$
String IRGEN_CLIENT_PLUGIN_VERSION = "IRGEN_CLIENT_PLUGIN_VERSION"; //$NON-NLS-1$
String ARSA_FILE_ID = "ARSAFileId"; //$NON-NLS-1$
String WIN_SCRIPT = "appscan.bat"; //$NON-NLS-1$
String FILE_ID = "ApplicationFileId"; //$NON-NLS-1$
String WIN_SCRIPT = "appscan.bat"; //$NON-NLS-1$
String UNIX_SCRIPT = "appscan.sh"; //$NON-NLS-1$

String IRX_EXTENSION = ".irx"; //$NON-NLS-1$
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/hcl/appscan/sdk/scanners/sast/SASTScan.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.hcl.appscan.sdk.error.ScannerException;
import com.hcl.appscan.sdk.logging.DefaultProgress;
import com.hcl.appscan.sdk.logging.IProgress;
import com.hcl.appscan.sdk.logging.Message;
import com.hcl.appscan.sdk.scan.IScanServiceProvider;
import com.hcl.appscan.sdk.scanners.ASoCScan;
import com.hcl.appscan.sdk.utils.ArchiveUtil;
Expand Down Expand Up @@ -47,6 +48,10 @@ public void run() throws ScannerException, InvalidTargetException {
if(target == null || !(new File(target).exists()))
throw new InvalidTargetException(Messages.getMessage(TARGET_INVALID, target));

if (getProperties().containsKey(OPEN_SOURCE_ONLY)){
getProgress().setStatus(new Message(Message.WARNING, Messages.getMessage(CoreConstants.WARNING_SCA)));
}

try {
if(getProperties().containsKey(CoreConstants.UPLOAD_DIRECT)){
generateZip();
Expand Down
101 changes: 101 additions & 0 deletions src/main/java/com/hcl/appscan/sdk/scanners/sca/SCAScan.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.hcl.appscan.sdk.scanners.sca;

import com.hcl.appscan.sdk.Messages;
import com.hcl.appscan.sdk.error.InvalidTargetException;
import com.hcl.appscan.sdk.error.ScannerException;
import com.hcl.appscan.sdk.logging.IProgress;
import com.hcl.appscan.sdk.scan.IScanServiceProvider;
import com.hcl.appscan.sdk.scanners.ASoCScan;
import com.hcl.appscan.sdk.scanners.sast.SAClient;
import com.hcl.appscan.sdk.scanners.sast.SASTConstants;

import java.io.File;
import java.io.IOException;
import java.net.Proxy;
import java.util.Map;

public class SCAScan extends ASoCScan implements SASTConstants {
private static final long serialVersionUID = 1L;
private static final String REPORT_FORMAT = "html"; //$NON-NLS-1$
private File m_irx;

public SCAScan(Map<String, String> properties, IProgress progress, IScanServiceProvider provider) {
super(properties, progress, provider);
}

@Override
public void run() throws ScannerException, InvalidTargetException {
String target = getTarget();

if(target == null || !(new File(target).exists()))
throw new InvalidTargetException(Messages.getMessage(TARGET_INVALID, target));

try {
generateIR();
analyzeIR();
} catch(IOException e) {
throw new ScannerException(Messages.getMessage(SCAN_FAILED, e.getLocalizedMessage()));
}
}

@Override
public String getType() {
return "Software Composition Analysis";
}

@Override
public String getReportFormat() {
return REPORT_FORMAT;
}

public File getIrx() {
return m_irx;
}

private void generateIR() throws IOException, ScannerException {
File targetFile = new File(getTarget());

//If we were given an irx file, don't generate a new one
if(targetFile.getName().endsWith(".irx") && targetFile.isFile()) {
m_irx = targetFile;
return;
}

//Get the target directory
String targetDir = targetFile.isDirectory() ? targetFile.getAbsolutePath() : targetFile.getParent();

//Create and run the process
Proxy proxy = getServiceProvider() == null ? Proxy.NO_PROXY : getServiceProvider().getAuthenticationProvider().getProxy();
new SAClient(getProgress(), proxy).run(targetDir, getProperties());
String irxDir = getProperties().containsKey(SAVE_LOCATION) ? getProperties().get(SAVE_LOCATION) : targetDir;
m_irx = new File(irxDir, getName() + IRX_EXTENSION);
if(!m_irx.isFile())
throw new ScannerException(Messages.getMessage(ERROR_GENERATING_IRX, getScanLogs().getAbsolutePath()));
}

private void analyzeIR() throws IOException, ScannerException {
if(getProperties().containsKey(PREPARE_ONLY))
return;

String fileId = getServiceProvider().submitFile(m_irx);
if(fileId == null)
throw new ScannerException(Messages.getMessage(ERROR_FILE_UPLOAD, m_irx.getName()));

Map<String, String> params = getProperties();
params.put(FILE_ID, fileId);

setScanId(getServiceProvider().createAndExecuteScan("Sca", params));
if(getScanId() == null)
throw new ScannerException(Messages.getMessage(ERROR_SUBMITTING_IRX));
}

private File getScanLogs() {
if(m_irx == null) {
return new File("logs"); //$NON-NLS-1$
}
String logsFile = m_irx.getName();
logsFile = logsFile.substring(0, logsFile.lastIndexOf(".")); //$NON-NLS-1$
logsFile += "_logs.zip"; //$NON-NLS-1$
return new File(m_irx.getParentFile(), logsFile);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/hcl/appscan/sdk/scanners/sca/SCAScanFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.hcl.appscan.sdk.scanners.sca;

import com.hcl.appscan.sdk.auth.IAuthenticationProvider;
import com.hcl.appscan.sdk.logging.IProgress;
import com.hcl.appscan.sdk.scan.CloudScanServiceProvider;
import com.hcl.appscan.sdk.scan.IScan;
import com.hcl.appscan.sdk.scan.IScanFactory;
import com.hcl.appscan.sdk.scan.IScanServiceProvider;
import com.hcl.appscan.sdk.scanners.sast.SASTScan;

import java.util.Map;

public class SCAScanFactory implements IScanFactory {

@Override
public IScan create(Map<String, String> properties, IProgress progress, IAuthenticationProvider authProvider) {
IScanServiceProvider serviceProvider = new CloudScanServiceProvider(progress, authProvider);
return new SCAScan(properties, progress, serviceProvider);
}

@Override
public String getType() {
return "Sca";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not SCA ? Conpare it with SAST

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we were using the "/api/v4/Scans/Sca" API to execute the scan. So, taken the "Sca" from there.

}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
com.hcl.appscan.sdk.scanners.sast.SASTScanFactory
com.hcl.appscan.sdk.scanners.dynamic.DASTScanFactory
com.hcl.appscan.sdk.scanners.ase.ASEScanFactory
com.hcl.appscan.sdk.scanners.sca.SCAScanFactory
Loading