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) #117

Merged
merged 10 commits into from
Nov 20, 2023
Merged
7 changes: 6 additions & 1 deletion src/main/java/com/hcl/appscan/sdk/CoreConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ public interface CoreConstants {
String USER_MESSAGE = "UserMessage"; //$NON-NLS-1$
String IS_VALID = "IsValid"; //$NON-NLS-1$
String SOURCE_CODE_ONLY = "sourceCodeOnly"; //$NON-NLS-1$
String CREATE_IRX = "createIRX"; //$NON-NLS-1$
String SOFTWARE_COMPOSITION_ANALYZER = "Software Composition Analyzer"; //$NON-NLS-1$
String SCA = "Sca"; //$NON-NLS-1$

String CREATE_IRX = "createIRX"; //$NON-NLS-1$
String UPLOAD_DIRECT = "uploadDirect"; //$NON-NLS-1$
String BINDING_ID = "Bindingid"; //$NON-NLS-1$
String APPSCAN_OPTS = "APPSCAN_OPTS"; //$NON-NLS-1$
Expand All @@ -50,6 +53,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 +67,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
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?

Copy link

Choose a reason for hiding this comment

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

There shouldn't be any need for that.

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 @@ -105,6 +109,39 @@ public HttpResponse post(String url,
return makeRequest(Method.POST, url, headerProperties, body);
}

/**
* Submit a post request.
*
* @param url The URL string.
* @param headerProperties An optional Map of header properties.
* @param params An optional Map of properties.
* @return The response as a byte array.
* @throws IOException If an error occurs.
*/
public HttpResponse post(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 put request.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,50 +48,60 @@ public CloudScanServiceProvider(IProgress progress, IAuthenticationProvider auth
m_progress = progress;
m_authProvider = authProvider;
}

@Override
public String createAndExecuteScan(String type, Map<String, String> params) {
if(loginExpired() || !verifyApplication(params.get(APP_ID)))
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);

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

try {
HttpResponse response = client.postForm(request_url, request_headers, params);
int status = response.getResponseCode();

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

if (status == HttpsURLConnection.HTTP_CREATED) {
m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATE_SCAN_SUCCESS)));
return json.getString(ID);
}
else if (json != null && json.has(MESSAGE)) {
String errorResponse = json.getString(MESSAGE);
if(json.has(FORMAT_PARAMS)) {
JSONArray jsonArray = json.getJSONArray(FORMAT_PARAMS);
if(jsonArray != null){
String[] messageParams = new String[jsonArray.size()];
for (int i = 0; i < jsonArray.size(); i++) {
messageParams[i] = (String)jsonArray.get(i);
}
errorResponse = MessageFormat.format(errorResponse, (Object[]) messageParams);
}

@Override
public String createAndExecuteScan(String type, Map<String, String> params) {
if(loginExpired() || !verifyApplication(params.get(APP_ID)))
return null;

m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(EXECUTING_SCAN)));
Map<String, String> request_headers = m_authProvider.getAuthorizationHeader(true);
HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts());

try {
HttpResponse response;
if(shouldUseV4Api(type)) {
request_headers.put("Content-Type", "application/json");
request_headers.put("accept", "application/json");
String request_url = m_authProvider.getServer() + String.format(API_SCANNER_V4, type);
response = client.post(request_url,request_headers,params);
} else {
String request_url = m_authProvider.getServer() + String.format(API_SCANNER, type);
response = client.postForm(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);
} else if (json != null && json.has(MESSAGE)) {
String errorResponse = json.getString(MESSAGE);
if(json.has(FORMAT_PARAMS)) {
JSONArray jsonArray = json.getJSONArray(FORMAT_PARAMS);
if(jsonArray != null){
String[] messageParams = new String[jsonArray.size()];
for (int i = 0; i < jsonArray.size(); i++) {
messageParams[i] = (String)jsonArray.get(i);
}
errorResponse = MessageFormat.format(errorResponse, (Object[]) messageParams);
}
}
m_progress.setStatus(new Message(Message.ERROR, errorResponse));
}
else
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_SUBMITTING_SCAN, status)));
} catch(IOException | JSONException e) {
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_SUBMITTING_SCAN, e.getLocalizedMessage())));
}
return null;
}
m_progress.setStatus(new Message(Message.ERROR, errorResponse));
}
else
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_SUBMITTING_SCAN, status)));
} catch(IOException | JSONException e) {
m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_SUBMITTING_SCAN, e.getLocalizedMessage())));
}
return null;
}

private boolean shouldUseV4Api(String type) {
return type.equals(CoreConstants.SCA);
}

@Override
public String submitFile(File file) throws IOException {
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(CoreConstants.SOFTWARE_COMPOSITION_ANALYZER))
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
15 changes: 10 additions & 5 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 @@ -73,7 +74,7 @@ public File getIrx() {
return m_irx;
}

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

//If we were given an irx file, don't generate a new one
Expand Down Expand Up @@ -108,7 +109,7 @@ private void generateZip() throws IOException,ScannerException {
throw new ScannerException(Messages.getMessage(ERROR_GENERATING_ZIP, getScanLogs().getAbsolutePath()));
}

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

Expand All @@ -117,9 +118,13 @@ private void analyzeIR() throws IOException, ScannerException {
throw new ScannerException(Messages.getMessage(ERROR_FILE_UPLOAD, m_irx.getName()));

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

setScanId(getServiceProvider().createAndExecuteScan(STATIC_ANALYZER, params));
params.put(FILE_ID, fileId);

if(getType().equals(CoreConstants.SOFTWARE_COMPOSITION_ANALYZER)) {
Copy link

@mattmurp mattmurp Nov 16, 2023

Choose a reason for hiding this comment

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

I suggest breaking this if/else case out into a separate method. In the SCAScan, you can override the method to do what we need for SCA.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, made the changes accordingly.

setScanId(getServiceProvider().createAndExecuteScan(CoreConstants.SCA, params));
} else {
setScanId(getServiceProvider().createAndExecuteScan(STATIC_ANALYZER, params));
}
if(getScanId() == null)
throw new ScannerException(Messages.getMessage(ERROR_SUBMITTING_IRX));
}
Expand Down
48 changes: 48 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,48 @@
/**
* © Copyright HCL Technologies Ltd. 2023.
* LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0
*/

package com.hcl.appscan.sdk.scanners.sca;
Copy link

Choose a reason for hiding this comment

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

This class is almost identical to the existing SASTScan class. It would be better to extend SASTScan and adjust as needed than to copy the code into a new class.

Copy link
Author

Choose a reason for hiding this comment

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

Okay, will do accordingly.


import com.hcl.appscan.sdk.CoreConstants;
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.sast.SASTConstants;
import com.hcl.appscan.sdk.scanners.sast.SASTScan;

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

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

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 CoreConstants.SOFTWARE_COMPOSITION_ANALYZER;
}
}
30 changes: 30 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,30 @@
/**
* © Copyright HCL Technologies Ltd. 2023.
* LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0
*/

package com.hcl.appscan.sdk.scanners.sca;

import com.hcl.appscan.sdk.CoreConstants;
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 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 CoreConstants.SOFTWARE_COMPOSITION_ANALYZER;
}
}
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