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

Added issue count for polaris and srm #20

Merged
merged 11 commits into from
Jan 13, 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.jenkins.plugins.security.scan.action;

import hudson.model.Action;
import io.jenkins.plugins.security.scan.global.ApplicationConstants;
import java.util.HashMap;
import java.util.Map;

public class IssueAction implements Action {
private final String product;
private final int defectCount;
private final String issueViewUrl;

private static final Map<String, String> PRODUCT_NAME_MAP;

static {
PRODUCT_NAME_MAP = new HashMap<>();
PRODUCT_NAME_MAP.put("polaris", "Polaris");
PRODUCT_NAME_MAP.put("srm", "SRM");
}

public IssueAction(final String product, final int defectCount, final String issueViewUrl) {
this.product = product;
this.defectCount = defectCount;
this.issueViewUrl = issueViewUrl;
}

@Override
public String getIconFileName() {
return ApplicationConstants.BLACK_DUCK_LOGO_FILE_NAME;
}

@Override
public String getDisplayName() {
return String.format("See %d issues in %s", defectCount, getDisplayNameForProduct(product));
}

@Override
public String getUrlName() {
return issueViewUrl;
}

private String getDisplayNameForProduct(String product) {
return PRODUCT_NAME_MAP.getOrDefault(product.toLowerCase(), product);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.jenkins.plugins.security.scan.action;

import hudson.FilePath;
import hudson.model.Action;

public class IssueActionItems implements Action {
private final String product;
private final FilePath filePath;

public IssueActionItems(String product, FilePath filePath) {
this.product = product;
this.filePath = filePath;
}

public String getProduct() {
return product;
}

public FilePath getFilePath() {
return filePath;
}

@Override
public String getIconFileName() {
return null;
}

@Override
public String getDisplayName() {
return null;
}

@Override
public String getUrlName() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import hudson.util.ListBoxModel;
import io.jenkins.plugins.security.scan.ScanInitializer;
import io.jenkins.plugins.security.scan.SecurityScanner;
import io.jenkins.plugins.security.scan.action.IssueActionItems;
import io.jenkins.plugins.security.scan.exception.PluginExceptionHandler;
import io.jenkins.plugins.security.scan.exception.ScannerException;
import io.jenkins.plugins.security.scan.extension.SecurityScan;
Expand Down Expand Up @@ -1148,15 +1149,16 @@ public void perform(
String undefinedErrorMessage = null;
Exception unknownException = new Exception();
LoggerWrapper logger = new LoggerWrapper(listener);
Map<String, Object> scanparametersMap = null;

logger.info(
"**************************** START EXECUTION OF BLACK DUCK SECURITY SCAN ****************************");

try {
scanparametersMap = getParametersMap(workspace, listener);
SecurityScanner securityScanner = new SecurityScanner(run, listener, launcher, workspace, envVars);
ScanInitializer scanInitializer = new ScanInitializer(securityScanner, workspace, envVars, listener);

exitCode = scanInitializer.initializeScanner(getParametersMap(workspace, listener));
exitCode = scanInitializer.initializeScanner(scanparametersMap);
} catch (Exception e) {
if (e instanceof PluginExceptionHandler) {
exitCode = ((PluginExceptionHandler) e).getCode();
Expand All @@ -1166,6 +1168,12 @@ public void perform(
unknownException = e;
}
} finally {
if (scanparametersMap != null && scanparametersMap.containsKey(ApplicationConstants.PRODUCT_KEY)) {
run.addAction(new IssueActionItems(
scanparametersMap.get(ApplicationConstants.PRODUCT_KEY).toString(),
workspace.child(ApplicationConstants.SCAN_INFO_OUT_FILE_NAME)));
}

String exitMessage = ExceptionMessages.getErrorMessage(exitCode, undefinedErrorMessage);
if (exitMessage != null) {
if (exitCode == 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.jenkins.plugins.security.scan.extension.global;

import com.fasterxml.jackson.databind.JsonNode;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import io.jenkins.plugins.security.scan.action.IssueAction;
import io.jenkins.plugins.security.scan.action.IssueActionItems;
import io.jenkins.plugins.security.scan.global.ApplicationConstants;
import io.jenkins.plugins.security.scan.global.LoggerWrapper;
import io.jenkins.plugins.security.scan.global.Utility;
import io.jenkins.plugins.security.scan.global.enums.SecurityProduct;
import java.io.File;

@Extension
public class SecurityScanRunListener extends RunListener<Run<?, ?>> {
private LoggerWrapper logger;

@Override
public void onCompleted(Run<?, ?> run, TaskListener listener) {
this.logger = new LoggerWrapper(listener);

IssueActionItems issueActionItems = run.getAction(IssueActionItems.class);
String product = issueActionItems != null ? issueActionItems.getProduct() : ApplicationConstants.NOT_AVAILABLE;

if (product.equals(SecurityProduct.POLARIS.name()) || product.equals(SecurityProduct.SRM.name())) {
try {
FilePath filePath = issueActionItems != null ? issueActionItems.getFilePath() : null;

if (filePath == null || !filePath.exists()) {
logger.error(ApplicationConstants.SCAN_INFO_FILE_NOT_FOUND);
}

File localFile = new File(filePath.getRemote());
JsonNode rootNode = Utility.parseJsonFile(localFile);
String issuesUrl = Utility.getIssuesUrl(rootNode, product.toLowerCase());
int totalIssues = Utility.calculateTotalIssues(rootNode, product.toLowerCase());

run.addAction(new IssueAction(product.toLowerCase(), totalIssues, issuesUrl != null ? issuesUrl : ""));
} catch (RuntimeException e) {
logger.error(ApplicationConstants.EXCEPTION_WHILE_PROCESS_SCAN_INFO_FILE);
} catch (Exception e) {
logger.error(ApplicationConstants.EXCEPTION_WHILE_PROCESS_SCAN_INFO_FILE);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.jenkins.plugins.gitlabbranchsource.GitLabSCMSource;
import io.jenkins.plugins.security.scan.ScanInitializer;
import io.jenkins.plugins.security.scan.SecurityScanner;
import io.jenkins.plugins.security.scan.action.IssueActionItems;
import io.jenkins.plugins.security.scan.exception.PluginExceptionHandler;
import io.jenkins.plugins.security.scan.exception.ScannerException;
import io.jenkins.plugins.security.scan.extension.SecurityScan;
Expand Down Expand Up @@ -1340,13 +1341,15 @@ protected Integer run() throws PluginExceptionHandler, ScannerException {
logger.println(
"**************************** START EXECUTION OF BLACK DUCK SECURITY SCAN ****************************");

Map<String, Object> scanparametersMap = getParametersMap(workspace, listener);

try {
verifyRequiredPlugins(logger, envVars);

SecurityScanner securityScanner = new SecurityScanner(run, listener, launcher, workspace, envVars);
ScanInitializer scanInitializer = new ScanInitializer(securityScanner, workspace, envVars, listener);

exitCode = scanInitializer.initializeScanner(getParametersMap(workspace, listener));
exitCode = scanInitializer.initializeScanner(scanparametersMap);
} catch (Exception e) {
if (e instanceof PluginExceptionHandler) {
exitCode = ((PluginExceptionHandler) e).getCode();
Expand All @@ -1356,6 +1359,9 @@ protected Integer run() throws PluginExceptionHandler, ScannerException {
unknownException = e;
}
} finally {
run.addAction(new IssueActionItems(
scanparametersMap.get(ApplicationConstants.PRODUCT_KEY).toString(),
workspace.child(ApplicationConstants.SCAN_INFO_OUT_FILE_NAME)));
String exitMessage = ExceptionMessages.getErrorMessage(exitCode, undefinedErrorMessage);
if (exitMessage != null) {
if (exitCode == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class ApplicationConstants {
public static final String BRIDGE_CLI_EXECUTABLE_WINDOWS = "bridge-cli.exe";
public static final String VERSION_FILE = "versions.txt";
public static final String NOT_AVAILABLE = "NA";
public static final String SCAN_INFO_OUT_FILE_NAME = ".bridge/output/scan_info_out.json";
public static final String BLACK_DUCK_LOGO_FILE_NAME = "/plugin/black-duck-security-scan/icons/blackduck.png";

// Jenkins Environment Variables
public static final String ENV_JOB_NAME_KEY = "JOB_NAME";
Expand Down Expand Up @@ -262,6 +264,11 @@ public class ApplicationConstants {
DEPRECATED_PRODUCT_WILL_BE_REMOVED_IN_FUTURE_AND_RECOMMENDING_TO_USE_NEW_PRODUCT_AND_ITS_PARAMETERS =
"%s product is deprecated and will be removed in future. Please use %s and its corresponding parameters instead.";

// Warn Log Related Issue Count
public static final String SCAN_INFO_FILE_NOT_FOUND = "File for issue count does not exist";
public static final String EXCEPTION_WHILE_PROCESS_SCAN_INFO_FILE =
"An exception occurred while processing JSON file for Issue count";

// Bridge Download Parameters
@Deprecated
public static final String SYNOPSYS_BRIDGE_DOWNLOAD_URL = "synopsys_bridge_download_url";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public class BridgeParams {
public static final String STAGE_OPTION = "--stage";
public static final String INPUT_OPTION = "--input";
public static final String OUT_OPTION = "--out";
public static final String DIAGNOSTICS_OPTION = "--diagnostics";

public static final String BLACKDUCKSCA_STAGE = "blackducksca";
Expand Down
67 changes: 67 additions & 0 deletions src/main/java/io/jenkins/plugins/security/scan/global/Utility.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package io.jenkins.plugins.security.scan.global;

import com.cloudbees.hudson.plugins.folder.Folder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.model.TopLevelItem;
import io.jenkins.plugins.security.scan.global.enums.BuildStatus;
import io.jenkins.plugins.security.scan.global.enums.IssueSeverities;
import io.jenkins.plugins.security.scan.global.enums.ScanType;
import io.jenkins.plugins.security.scan.global.enums.SecurityProduct;
import java.io.File;
import java.io.IOException;
import java.net.*;
Expand All @@ -18,6 +23,13 @@

public class Utility {

public static final String DATA_PROPERTY = "data";
public static final String PROJECT_PROPERTY = "project";
public static final String ISSUES_PROPERTY = "issues";
public static final String URL_PROPERTY = "url";
public static final String TEST_PROPERTY = "test";
public static final String ANALYSIS_PROPERTY = "analysis";

public static String getDirectorySeparator(FilePath workspace, TaskListener listener) {
String os = getAgentOs(workspace, listener);

Expand Down Expand Up @@ -295,6 +307,61 @@ public static String extractVersionFromUrl(String url) {
return version;
}

public static JsonNode parseJsonFile(File file) {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.readTree(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static String getIssuesUrl(JsonNode rootNode, String product) {
JsonNode productNode = rootNode.path(DATA_PROPERTY).path(product);
if (!productNode.isMissingNode()) {
JsonNode issuesUrlNode =
productNode.path(PROJECT_PROPERTY).path(ISSUES_PROPERTY).path(URL_PROPERTY);
if (!issuesUrlNode.isMissingNode()) {
return issuesUrlNode.asText();
}
}
return null;
}

public static int calculateTotalIssues(JsonNode rootNode, String product) {
int totalIssues = 0;
JsonNode productNode = rootNode.path(DATA_PROPERTY).path(product);
if (!productNode.isMissingNode()) {
if (SecurityProduct.SRM.name().equalsIgnoreCase(product)) {
JsonNode analysisNode = productNode.path(ANALYSIS_PROPERTY);
if (!analysisNode.isMissingNode()) {
totalIssues = calculateIssues(analysisNode);
}
} else if (SecurityProduct.POLARIS.name().equalsIgnoreCase(product)) {
JsonNode testNode = productNode.path(TEST_PROPERTY);
for (ScanType scanType : ScanType.values()) {
totalIssues += calculateIssues(testNode.path(scanType.name()));
}
}
}

return totalIssues;
}

public static int calculateIssues(JsonNode testNode) {
if (!testNode.isMissingNode()) {
JsonNode issuesNode = testNode.path(ISSUES_PROPERTY);
if (!issuesNode.isMissingNode()) {
int total = 0;
for (IssueSeverities severity : IssueSeverities.values()) {
total += issuesNode.path(severity.name().toLowerCase()).asInt(0);
}
return total;
}
}
return 0;
}

public static boolean isBoolean(String value) {
return value.equals("true") || value.equals("false");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.jenkins.plugins.security.scan.global.enums;

public enum IssueSeverities {
CRITICAL,
HIGH,
INFO,
LOW,
MEDIUM,
UNSPECIFIED;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.jenkins.plugins.security.scan.global.enums;

public enum ScanType {
SAST,
SCA;
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ private void setPolarisCommands(
scanCommands.add(BridgeParams.INPUT_OPTION);
scanCommands.add(prepareBridgeInputJson(
scanParameters, polaris, scmObject, ApplicationConstants.POLARIS_INPUT_JSON_PREFIX, project));
scanCommands.add(BridgeParams.OUT_OPTION);
scanCommands.add(ApplicationConstants.SCAN_INFO_OUT_FILE_NAME);
}
}

Expand All @@ -178,6 +180,8 @@ private void setSrmCommands(
scanCommands.add(BridgeParams.INPUT_OPTION);
scanCommands.add(prepareBridgeInputJson(
scanParameters, srm, scmObject, ApplicationConstants.SRM_INPUT_JSON_PREFIX, project));
scanCommands.add(BridgeParams.OUT_OPTION);
scanCommands.add(ApplicationConstants.SCAN_INFO_OUT_FILE_NAME);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
<l:layout title="${my.displayName}" permission="${it.READ}">
<h1>${my.displayName}</h1>
<img src="${rootURL}${my.iconFileName}" alt="Black Duck Logo" />
</l:layout>
</j:jelly>
Binary file added src/main/webapp/icons/blackduck.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading