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

DAST rescan #236

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
93187be
dast-rescan
vishalhcl-5960 Oct 16, 2024
a6e7a87
Update DynamicAnalyzer.java
vishalhcl-5960 Oct 19, 2024
5ae512d
Update DynamicAnalyzer.java
vishalhcl-5960 Oct 21, 2024
aa98bd6
URL validation check for A360
vishalhcl-5960 Nov 5, 2024
a7ea86f
updated scanId validation method
vishalhcl-5960 Nov 8, 2024
4a405fa
UI scanId validation
vishalhcl-5960 Nov 11, 2024
4ce66b7
Code enhancement for better UI
vishalhcl-5960 Nov 19, 2024
54e4c81
indentation checks & bug fixes
vishalhcl-5960 Nov 22, 2024
372de05
updated the scanId validation method
vishalhcl-5960 Nov 26, 2024
b3797ba
A360 version check for URL validation
vishalhcl-5960 Nov 29, 2024
408208a
UI URL validation for A360 connection
vishalhcl-5960 Nov 29, 2024
16b7a85
A360 version check for URL validation
vishalhcl-5960 Dec 3, 2024
b686bdb
As per PR comments
vishalhcl-5960 Dec 4, 2024
193d928
Addressing PR comments
vishalhcl-5960 Dec 6, 2024
66a62fb
Moved the common validation method to the base scanner class
vishalhcl-5960 Dec 6, 2024
ffb79e6
UI validation for base scan field
vishalhcl-5960 Dec 10, 2024
63b9f39
As per comments
vishalhcl-5960 Dec 12, 2024
9d05f21
indentation checks
vishalhcl-5960 Dec 12, 2024
d75f3cc
ASA-10068
Vishal5960 Dec 14, 2024
c8bf28e
ASA-10067
vishalhcl-5960 Dec 15, 2024
2a1c29f
Merge branch 'DAST-rescan' of https://github.com/jenkinsci/appscan-pl…
vishalhcl-5960 Dec 15, 2024
c9b1522
Update DynamicAnalyzer.java
vishalhcl-5960 Dec 15, 2024
cb9ddc5
Updated the warning message statement
vishalhcl-5960 Dec 16, 2024
c4e1cbd
ASA-9275
vishalhcl-5960 Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -308,46 +308,6 @@ private void shouldFailBuild(IResultsProvider provider,Run<?,?> build) throws Ab
throw new AbortException(Messages.error_checking_results(provider.getStatus()));
}
}

private void validateGeneralSettings(boolean isAppScan360, Map<String, String> properties, IProgress progress) throws IOException {
if(isAppScan360) {
if (m_intervention) {
progress.setStatus(new Message(Message.WARNING, Messages.warning_allow_intervention_AppScan360()));
}
} else if (m_authProvider.getacceptInvalidCerts()) {
progress.setStatus(new Message(Message.WARNING, Messages.warning_asoc_certificates()));
}

if(properties.containsKey(CoreConstants.OPEN_SOURCE_ONLY)) {
progress.setStatus(new Message(Message.WARNING, Messages.warning_sca()));
m_scanner = ScannerFactory.getScanner(Scanner.SOFTWARE_COMPOSITION_ANALYZER, properties.get(CoreConstants.TARGET));
properties.put(CoreConstants.SCANNER_TYPE, CoreConstants.SOFTWARE_COMPOSITION_ANALYZER);
}

if(properties.containsKey(CoreConstants.SCAN_ID)) {
if(properties.get(CoreConstants.PERSONAL_SCAN).equals("true")) {
progress.setStatus(new Message(Message.WARNING, Messages.warning_personal_scan_rescan()));
}

try {
scanIdValidation(properties,progress);
} catch (JSONException e) {
//Ignore and move on.
}
}
}

private void scanIdValidation(Map<String, String> properties, IProgress progress) throws JSONException, IOException {
IScanServiceProvider scanServiceProvider = new CloudScanServiceProvider(progress, m_authProvider);
JSONObject scanDetails = scanServiceProvider.getScanDetails(properties.get(CoreConstants.SCAN_ID));
if(scanDetails == null) {
throw new AbortException(Messages.error_invalid_scan_id());
} else if (!scanDetails.get(CoreConstants.APP_ID).equals(properties.get(CoreConstants.APP_ID))) {
throw new AbortException(Messages.error_invalid_scan_id_application());
} else if (!scanDetails.get("Technology").equals(ServiceUtil.updatedScanType(properties.get(CoreConstants.SCANNER_TYPE)))) {
throw new AbortException(Messages.error_invalid_scan_id_scan_type());
}
}

private void perform(Run<?,?> build, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {
m_authProvider = new JenkinsAuthenticationProvider(m_credentials, build.getParent().getParent());
Expand All @@ -356,8 +316,13 @@ private void perform(Run<?,?> build, Launcher launcher, TaskListener listener) t
Map<String, String> properties = getScanProperties(build,listener);
boolean isAppScan360 = ((JenkinsAuthenticationProvider) m_authProvider).isAppScan360();

m_scanner.validateSettings((JenkinsAuthenticationProvider) m_authProvider,properties, progress);
validateGeneralSettings(isAppScan360,properties,progress);
m_scanner.validateSettings((JenkinsAuthenticationProvider) m_authProvider,properties, progress, isAppScan360);

if (properties.containsKey(CoreConstants.OPEN_SOURCE_ONLY)) {
progress.setStatus(new Message(Message.WARNING, Messages.warning_sca()));
m_scanner = ScannerFactory.getScanner(Scanner.SOFTWARE_COMPOSITION_ANALYZER, properties.get(CoreConstants.TARGET));
properties.put(CoreConstants.SCANNER_TYPE, CoreConstants.SOFTWARE_COMPOSITION_ANALYZER);
}


final IScan scan = ScanFactory.createScan(properties, progress, m_authProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@

package com.hcl.appscan.jenkins.plugin.scanners;

import java.io.IOException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

import com.hcl.appscan.sdk.CoreConstants;
import com.hcl.appscan.sdk.logging.IProgress;
import com.hcl.appscan.sdk.scan.CloudScanServiceProvider;
import org.apache.wink.json4j.JSONArray;
import org.apache.wink.json4j.JSONException;
import org.apache.wink.json4j.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
Expand Down Expand Up @@ -41,6 +50,8 @@ public class DynamicAnalyzer extends Scanner {

private static final String DYNAMIC_ANALYZER = "Dynamic Analyzer"; //$NON-NLS-1$

private boolean m_incrementalScan;
private String m_executionId;
private String m_presenceId;
private String m_scanFile;
private String m_scanType;
Expand All @@ -50,15 +61,21 @@ public class DynamicAnalyzer extends Scanner {
private String m_loginUser;
private Secret m_loginPassword;
private String m_trafficFile;
private boolean m_rescanDast;
private String m_scanId;

@Deprecated
public DynamicAnalyzer(String target) {
this(target, false, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY);
this(target, false, false, EMPTY, false, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY);
}

@Deprecated
public DynamicAnalyzer(String target, boolean hasOptions, String presenceId, String scanFile, String scanType, String optimization, String extraField, String loginUser, String loginPassword, String trafficFile, String loginType) {
public DynamicAnalyzer(String target, boolean hasOptions, boolean rescanDast, String scanId, boolean incrementalScan, String executionId, String presenceId, String scanFile, String scanType, String optimization, String extraField, String loginUser, String loginPassword, String trafficFile, String loginType) {
super(target, hasOptions);
m_rescanDast = rescanDast;
m_scanId = scanId;
m_incrementalScan = incrementalScan;
m_executionId = incrementalScan ? executionId : EMPTY;
m_presenceId = presenceId;
m_scanFile = scanFile;
m_scanType = scanFile != null && !scanFile.equals(EMPTY) ? CUSTOM : scanType;
Expand All @@ -74,6 +91,8 @@ public DynamicAnalyzer(String target, boolean hasOptions, String presenceId, Str

public DynamicAnalyzer(String target, boolean hasOptions) {
super(target, hasOptions);
m_rescanDast = false;
m_scanId = EMPTY;
m_presenceId = EMPTY;
m_scanFile = EMPTY;
m_scanType = EMPTY;
Expand Down Expand Up @@ -103,6 +122,41 @@ public String getLoginPassword() {
return Secret.toString(m_loginPassword);
}

@DataBoundSetter
public void setRescanDast(boolean rescanDast) {
m_rescanDast = rescanDast;
}

public boolean getRescanDast() {
return m_rescanDast;
}

@DataBoundSetter
public void setScanId(String scanId) {
m_scanId = scanId;
}
public String getScanId() {
return m_scanId;
}

@DataBoundSetter
public void setIncrementalScan(boolean incrementalScan) {
m_incrementalScan = incrementalScan;
}

public boolean getIncrementalScan() {
return m_incrementalScan;
}

@DataBoundSetter
public void setExecutionId(String executionId) {
m_executionId = m_incrementalScan ? executionId : EMPTY;
}

public String getExecutionId() {
return m_executionId;
}

@DataBoundSetter
public void setPresenceId(String presenceId) {
m_presenceId = presenceId;
Expand Down Expand Up @@ -194,17 +248,39 @@ public String upgradeLoginScenario(){
}
}

public void validateSettings(JenkinsAuthenticationProvider authProvider, Map<String, String> properties, IProgress progress) throws AbortException {
public void validateSettings(JenkinsAuthenticationProvider authProvider, Map<String, String> properties, IProgress progress, boolean isAppScan360) throws IOException {
if(!ServiceUtil.hasDastEntitlement(authProvider)) {
throw new AbortException(Messages.error_active_subscription_validation(getType()));
}
if (authProvider.isAppScan360() && properties.containsKey(Scanner.PRESENCE_ID)) {
throw new AbortException(Messages.error_presence_AppScan360());
}
if (!authProvider.isAppScan360() && !properties.containsKey(Scanner.PRESENCE_ID) && !ServiceUtil.isValidUrl(properties.get(TARGET), authProvider, authProvider.getProxy())) {
if(getRescanDast()) {
if(!properties.containsKey(CoreConstants.SCAN_ID)) {
throw new AbortException(Messages.error_empty_scan_id());
} else if (m_incrementalScan && !properties.containsKey("IncrementalBaseJobId")) {
throw new AbortException(Messages.error_empty_execution_id());
}
}
if (authProvider.isAppScan360()) {
if (properties.containsKey(Scanner.PRESENCE_ID)) {
throw new AbortException(Messages.error_presence_AppScan360());
} else if (ServiceUtil.getServiceVersion(authProvider).substring(0,5).compareTo("1.4.0") != -1) {
if (!ServiceUtil.isValidUrl(properties.get(TARGET), authProvider, authProvider.getProxy())) {
throw new AbortException(Messages.error_url_validation(properties.get(TARGET)));
}
}
}
if (!getRescanDast() && !authProvider.isAppScan360() && !properties.containsKey(Scanner.PRESENCE_ID) && !ServiceUtil.isValidUrl(properties.get(TARGET), authProvider, authProvider.getProxy())) {
throw new AbortException(Messages.error_url_validation(properties.get(TARGET)));
}
}
validateGeneralSettings(authProvider, properties, progress, isAppScan360);
if(properties.containsKey(CoreConstants.SCAN_ID)) {
try {
JSONObject scanDetails = ServiceUtil.getScanDetails(DYNAMIC_ANALYZER, properties.get(CoreConstants.SCAN_ID), authProvider);
scanIdValidation(scanDetails, properties);
} catch (JSONException e) {
//Ignore and move on.
}
}
}

@Override
public Map<String, String> getProperties(VariableResolver<String> resolver) throws AbortException {
Expand Down Expand Up @@ -259,7 +335,13 @@ public Map<String, String> getProperties(VariableResolver<String> resolver) thro
if (!m_presenceId.equals(EMPTY)) {
properties.put(PRESENCE_ID, m_presenceId);
}

if(getRescanDast() && isNullOrEmpty(getScanId()) ){
properties.put(CoreConstants.SCAN_ID,getScanId());
if(m_incrementalScan && isNullOrEmpty(m_executionId)) {
properties.put("IncrementalBaseJobId", m_executionId);
properties.put("IsIncrementalRetest", "true");
}
}
return properties;
}

Expand All @@ -286,6 +368,21 @@ public String getDisplayName() {
return "Dynamic Analysis (DAST)";
}

public ListBoxModel doFillExecutionIdItems(@RelativePath("..") @QueryParameter String credentials, @AncestorInPath ItemGroup<?> context, @QueryParameter String scanId) throws JSONException {
IAuthenticationProvider authProvider = new JenkinsAuthenticationProvider(credentials, context);
JSONArray executionDetails = new CloudScanServiceProvider(authProvider).getBaseScanDetails(scanId, authProvider);
ListBoxModel model = new ListBoxModel();
if(executionDetails != null) {
for(int i = 0; i < executionDetails.length(); i++) {
JSONObject value = executionDetails.getJSONObject(i);
ZonedDateTime zdt = ZonedDateTime.parse((String) value.get("CreatedAt")).withZoneSameInstant(ZoneId.systemDefault());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM dd, yyyy, hh:mm a, z");
model.add(zdt.format(formatter), (String) value.get("Id"));
}
}
return model;
}

public ListBoxModel doFillScanTypeItems() {
ListBoxModel model = new ListBoxModel();
model.add(Messages.option_staging(), STAGING);
Expand Down Expand Up @@ -335,17 +432,44 @@ public FormValidation doCheckScanFile(@QueryParameter String scanFile) {
return FormValidation.ok();
}

public FormValidation doCheckTarget(@QueryParameter String target,@RelativePath("..") @QueryParameter String credentials, @AncestorInPath ItemGroup<?> context, @QueryParameter String presenceId) {
public FormValidation doCheckTarget(@QueryParameter String target,@RelativePath("..") @QueryParameter String credentials, @AncestorInPath ItemGroup<?> context, @QueryParameter String presenceId, @QueryParameter boolean rescanDast) {
JenkinsAuthenticationProvider authProvider = new JenkinsAuthenticationProvider(credentials,context);
if(!ServiceUtil.hasDastEntitlement(authProvider)) {
return FormValidation.error(Messages.error_active_subscription_validation_ui());
}
if(!authProvider.isAppScan360() && presenceId != null && presenceId.equals(EMPTY) && !target.equals(EMPTY) && !ServiceUtil.isValidUrl(target, authProvider, authProvider.getProxy())) {
if(!rescanDast && !authProvider.isAppScan360() && presenceId != null && presenceId.equals(EMPTY) && !target.equals(EMPTY) && !ServiceUtil.isValidUrl(target, authProvider, authProvider.getProxy())) {
return FormValidation.error(Messages.error_url_validation_ui());
}
if (authProvider.isAppScan360() && (ServiceUtil.getServiceVersion(authProvider).substring(0,5).compareTo("1.4.0") != -1)) {
if (!target.equals(EMPTY) && !ServiceUtil.isValidUrl(target, authProvider, authProvider.getProxy())) {
return FormValidation.error(Messages.error_url_validation_ui());
}
}
if(rescanDast) {
return FormValidation.ok();
}
return FormValidation.validateRequired(target);
}

public FormValidation doCheckScanId(@QueryParameter String scanId, @RelativePath("..") @QueryParameter String application, @RelativePath("..") @QueryParameter String credentials, @AncestorInPath ItemGroup<?> context) throws JSONException {
Copy link
Contributor

Choose a reason for hiding this comment

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

Much of the same logic is repeated numerous times in each of the scanners doCheckScanId() method. Common logic should be placed in the base class and only scanner specific logic should be in the scanner classes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As this method for doing the UI validations, we can't have the same validations in the abstract scanner class.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is that the case? We could add a protected static method to the Scanner base class that performed all of the common checks. That method would then be called in each of the subclasses doCheckScanId() method, along with any scanner specific checks that are needed for that technology.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As the "DescriptorImpl" class of each scan-type extends the "ScanDescriptor" class. I will add a new method in common class which will handle all the common validations for scanId.
protected FormValidation scanIdValidation(JSONObject scanDetails, String application)

JenkinsAuthenticationProvider provider = new JenkinsAuthenticationProvider(credentials, context);
if(scanId!=null && !scanId.isEmpty()) {
JSONObject scanDetails = ServiceUtil.getScanDetails(DYNAMIC_ANALYZER, scanId, provider);
return scanIdValidation(scanDetails, application);
}
return FormValidation.validateRequired(scanId);
}

public FormValidation doCheckExecutionId(@RelativePath("..") @QueryParameter String credentials, @AncestorInPath ItemGroup<?> context, @QueryParameter String scanId, @QueryParameter String executionId) {
IAuthenticationProvider authProvider = new JenkinsAuthenticationProvider(credentials, context);
JSONArray executionDetails = new CloudScanServiceProvider(authProvider).getBaseScanDetails(scanId, authProvider);
if(executionDetails == null) {
return FormValidation.error(Messages.error_base_scan_empty_ui());
} else {
return FormValidation.validateRequired(executionId);
}
}

public FormValidation doCheckPresenceId(@RelativePath("..") @QueryParameter String credentials, @AncestorInPath ItemGroup<?> context, @QueryParameter String presenceId) {
JenkinsAuthenticationProvider authProvider = new JenkinsAuthenticationProvider(credentials,context);
if(authProvider.isAppScan360()){
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
package com.hcl.appscan.jenkins.plugin.scanners;

import com.hcl.appscan.jenkins.plugin.Messages;
import com.hcl.appscan.sdk.CoreConstants;
import com.hcl.appscan.sdk.utils.ServiceUtil;
import hudson.model.Descriptor;
import hudson.util.FormValidation;
import org.apache.wink.json4j.JSONException;
import org.apache.wink.json4j.JSONObject;

public abstract class ScanDescriptor extends Descriptor<Scanner> {
public ScanDescriptor() {
}

protected FormValidation scanIdValidation(JSONObject scanDetails, String application) throws JSONException {
if(scanDetails == null) {
return FormValidation.error(Messages.error_invalid_scan_id_ui());
} else {
String status = scanDetails.getJSONObject("LatestExecution").getString("Status");
if (!(status.equals("Ready") || status.equals("Paused") || status.equals("Failed"))) {
return FormValidation.error(Messages.error_scan_id_validation_status(status));
} else if (!scanDetails.get("RescanAllowed").equals(true) && scanDetails.get("ParsedFromUploadedFile").equals(true)) {
return FormValidation.error(Messages.error_invalid_scan_id_rescan_allowed_ui());
} else if (!scanDetails.get(CoreConstants.APP_ID).equals(application)) {
return FormValidation.error(Messages.error_invalid_scan_id_application_ui());
}
}
return FormValidation.ok();
}
}
Loading
Loading