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,30 @@
package io.jenkins.plugins.security.scan.action;

import hudson.model.Action;

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

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 "/plugin/blackduck-security-scan/icons/blackduck.png";
}

@Override
public String getDisplayName() {
return "See issues in " + product + " (" + defectCount + " found)";
}

@Override
public String getUrlName() {
return issueViewUrl;

Check warning on line 28 in src/main/java/io/jenkins/plugins/security/scan/action/IssueAction.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 10-28 are not covered by tests
}
}
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;

Check warning on line 35 in src/main/java/io/jenkins/plugins/security/scan/action/IssueActionItems.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 10-35 are not covered by tests
}
}
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,7 @@ 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";

// Jenkins Environment Variables
public static final String ENV_JOB_NAME_KEY = "JOB_NAME";
Expand Down Expand Up @@ -262,6 +263,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
59 changes: 59 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 Down Expand Up @@ -295,6 +300,60 @@
return version;
}

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

Check warning on line 308 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 307-308 are not covered by tests
}
}

public static String getIssuesUrl(JsonNode rootNode, String product) {
JsonNode productNode = rootNode.path("data").path(product);
if (!productNode.isMissingNode()) {
JsonNode issuesUrlNode = productNode.path("project").path("issues").path("url");
if (!issuesUrlNode.isMissingNode()) {

Check warning on line 316 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 316 is only partially covered, one branch is missing
return issuesUrlNode.asText();
}
}
return null;
}

public static int calculateTotalIssues(JsonNode rootNode, String product) {
int totalIssues = 0;
JsonNode productNode = rootNode.path("data").path(product);
if (!productNode.isMissingNode()) {

Check warning on line 326 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 326 is only partially covered, one branch is missing
if (SecurityProduct.SRM.name().toLowerCase().equals(product)) {

Check warning on line 327 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 327 is only partially covered, one branch is missing
JsonNode analysisNode = productNode.path("analysis");
if (!analysisNode.isMissingNode()) {
totalIssues = calculateIssues(analysisNode);

Check warning on line 330 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 328-330 are not covered by tests
}
} else if (SecurityProduct.POLARIS.name().toLowerCase().equals(product)) {

Check warning on line 332 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 332 is only partially covered, one branch is missing
JsonNode testNode = productNode.path("test");
for (ScanType scanType : ScanType.values()) {
totalIssues += calculateIssues(testNode.path(scanType.name()));
}
}
}

return totalIssues;
}

public static int calculateIssues(JsonNode testNode) {
if (!testNode.isMissingNode()) {

Check warning on line 344 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 344 is only partially covered, one branch is missing
JsonNode issuesNode = testNode.path("issues");
if (!issuesNode.isMissingNode()) {

Check warning on line 346 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 346 is only partially covered, one branch is missing
int total = 0;
for (IssueSeverities severity : IssueSeverities.values()) {
total += issuesNode.path(severity.name().toLowerCase()).asInt(0);
}
return total;
}
}
return 0;

Check warning on line 354 in src/main/java/io/jenkins/plugins/security/scan/global/Utility.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 354 is not covered by tests
}

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