WhiteSource (Mend) Integration in Java

Introduction

WhiteSource (now Mend) is a comprehensive Software Composition Analysis (SCA) tool that automatically scans open-source components for security vulnerabilities, license compliance issues, and quality problems. This guide covers complete integration strategies for Java projects.

Mend Unified Agent Integration

Maven Configuration

<!-- pom.xml Mend Integration -->
<project>
<properties>
<whitesource.version>21.11.1</whitesource.version>
<whitesource.orgToken>${whitesource.orgToken}</whitesource.orgToken>
<whitesource.product>Your-Product-Name</whitesource.product>
<whitesource.project>${project.artifactId}</whitesource.project>
</properties>
<build>
<plugins>
<!-- Mend Unified Agent Plugin -->
<plugin>
<groupId>org.whitesource</groupId>
<artifactId>whitesource-maven-plugin</artifactId>
<version>${whitesource.version}</version>
<configuration>
<orgToken>${whitesource.orgToken}</orgToken>
<product>${whitesource.product}</product>
<productVersion>${project.version}</productVersion>
<project>${whitesource.project}</project>
<checkPolicies>true</checkPolicies>
<forceCheckAllDependencies>true</forceCheckAllDependencies>
<forceUpdate>true</forceUpdate>
<aggregateModules>true</aggregateModules>
<failOnError>true</failOnError>
<failOnPolicyViolation>true</failOnPolicyViolation>
<includePlugins>false</includePlugins>
<includeSubProjects>true</includeSubProjects>
<scanComment>Scan triggered by Maven build</scanComment>
<configurationDirectory>${project.basedir}</configurationDirectory>
<configurationFilePath>whitesource.config</configurationFilePath>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>update</goal>
<goal>check-policies</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Alternative: Using Maven plugin for basic scanning -->
<plugin>
<groupId>org.whitesource</groupId>
<artifactId>whitesource-maven-plugin</artifactId>
<version>${whitesource.version}</version>
<configuration>
<orgToken>${whitesource.orgToken}</orgToken>
<product>${whitesource.product}</product>
<checkPolicies>true</checkPolicies>
<failOnError>true</failOnError>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>whitesource-scan</id>
<build>
<plugins>
<plugin>
<groupId>org.whitesource</groupId>
<artifactId>whitesource-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>update</goal>
<goal>check-policies</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

Gradle Configuration

// build.gradle
plugins {
id 'org.whitesource' version '21.11.1'
}
whitesource {
orgToken = System.getenv('WHITESOURCE_ORG_TOKEN')
product = 'Your-Product-Name'
productVersion = project.version
project = project.name
checkPolicies = true
forceUpdate = true
failOnError = true
failOnPolicyViolation = true
includeSubProjects = true
aggregateModules = true
configurationPath = 'whitesource.config'
}
// Custom task for Mend scanning
task mendScan(dependsOn: whitesourceUpdate) {
doLast {
println "Mend scan completed for project: ${project.name}"
}
}
task mendCheckPolicies(dependsOn: whitesourceCheckPolicies) {
doLast {
println "Mend policy check completed"
}
}
// Task to generate Mend report
task generateMendReport {
doLast {
def reportDir = file("${buildDir}/reports/mend")
reportDir.mkdirs()
def reportFile = new File(reportDir, "mend-scan-summary.txt")
reportFile.text = """
Mend Security Scan Report
=========================
Project: ${project.name}
Version: ${project.version}
Scan Date: ${new Date()}
Configuration:
- Product: ${whitesource.product}
- Check Policies: ${whitesource.checkPolicies}
- Fail on Error: ${whitesource.failOnError}
"""
}
}
// Dependency for the scan process
whitesourceUpdate.finalizedBy generateMendReport

Unified Agent Configuration

whitesource.config

# Mend Unified Agent Configuration
apiKey=your-org-api-key
productName=Your-Product-Name
projectName=your-project-name
productToken=your-product-token
# Scan Configuration
forceUpdate=true
forceCheckAllDependencies=true
checkPolicies=true
appPath=.
includes=**/*.jar **/*.war **/*.ear **/*.zip
excludes=**/test-classes/** **/test/** **/target/test-classes/** **/target/test/**
# Project Configuration
projectVersion=1.0.0
projectToken=your-project-token
[email protected]
# Policy Management
failOnError=true
failOnPolicyViolation=true
forceCheckAllDependencies=true
ignoreSourceFiles=true
# Logging and Reporting
log.level=INFO
log.files.count=5
log.files.size=10
log.files.path=./whitesource-logs
# Proxy Configuration (if needed)
#proxy.host=proxy.company.com
#proxy.port=8080
#proxy.user=username
#proxy.password=password
# Advanced Configuration
#updateType=OVERRIDE
#updateInventory=false
#offline=false
#docker.scanImages=false
#docker.includes=**/*.tar **/*.tar.gz
#docker.excludes=**/*test* **/*latest*

Java-based Configuration Manager

package com.company.mend;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class MendConfigManager {
private final Properties config;
private final String configPath;
public MendConfigManager(String configPath) {
this.configPath = configPath;
this.config = new Properties();
loadConfiguration();
}
public void loadConfiguration() {
try (FileInputStream input = new FileInputStream(configPath)) {
config.load(input);
System.out.println("Loaded Mend configuration from: " + configPath);
} catch (IOException e) {
System.err.println("Failed to load Mend configuration: " + e.getMessage());
setDefaultConfiguration();
}
}
public void saveConfiguration() {
try (FileOutputStream output = new FileOutputStream(configPath)) {
config.store(output, "Mend Unified Agent Configuration");
System.out.println("Saved Mend configuration to: " + configPath);
} catch (IOException e) {
System.err.println("Failed to save Mend configuration: " + e.getMessage());
}
}
private void setDefaultConfiguration() {
// Basic configuration
config.setProperty("apiKey", "${env.MEND_API_KEY}");
config.setProperty("productName", "Default-Product");
config.setProperty("projectName", "default-project");
config.setProperty("checkPolicies", "true");
config.setProperty("forceUpdate", "true");
config.setProperty("failOnError", "true");
config.setProperty("appPath", ".");
config.setProperty("includes", "**/*.jar **/*.war **/*.ear");
config.setProperty("excludes", "**/test-classes/** **/test/**");
config.setProperty("log.level", "INFO");
}
public void setProjectConfiguration(String projectName, String projectVersion) {
config.setProperty("projectName", projectName);
config.setProperty("projectVersion", projectVersion);
config.setProperty("projectToken", generateProjectToken(projectName));
}
public void configureForCI() {
config.setProperty("checkPolicies", "true");
config.setProperty("failOnPolicyViolation", "true");
config.setProperty("forceCheckAllDependencies", "true");
config.setProperty("requesterEmail", "[email protected]");
}
public void configureForLocal() {
config.setProperty("checkPolicies", "false");
config.setProperty("failOnPolicyViolation", "false");
config.setProperty("requesterEmail", System.getProperty("user.name") + "@company.com");
}
private String generateProjectToken(String projectName) {
return "project-token-" + projectName.hashCode();
}
public Properties getConfig() {
return new Properties(config);
}
public String getConfigAsString() {
StringBuilder sb = new StringBuilder();
config.forEach((key, value) -> 
sb.append(key).append("=").append(value).append("\n"));
return sb.toString();
}
}

CI/CD Pipeline Integration

Jenkins Pipeline

// Jenkinsfile with Mend Integration
pipeline {
agent any
environment {
MEND_ORG_TOKEN = credentials('mend-org-token')
MEND_PRODUCT = 'Your-Product-Name'
MEND_API_URL = 'https://saas.mend.io'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean compile -DskipTests'
}
}
stage('Mend Scan') {
steps {
script {
// Download Unified Agent
sh '''
curl -L -o /tmp/whitesource-agent.jar \
"https://github.com/whitesource/fs-agent-distribution/raw/master/standalone/whitesource-fs-agent-21.11.1.jar"
'''
// Run Mend Scan
sh """
java -jar /tmp/whitesource-agent.jar -c whitesource.config \
-apiKey ${MEND_ORG_TOKEN} \
-product ${MEND_PRODUCT} \
-project ${env.JOB_NAME} \
-projectVersion ${env.BUILD_NUMBER} \
-d . \
-wss.url ${MEND_API_URL}
"""
}
}
post {
always {
// Archive Mend reports
archiveArtifacts artifacts: 'whitesource/*.json, whitesource/*.html', allowEmptyArchive: true
mendPublishResults scanFile: 'whitesource/scanResult.json'
}
success {
// Send success notification
emailext (
subject: "Mend Scan PASSED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Mend security scan completed successfully for ${env.JOB_NAME}
Build: ${env.BUILD_URL}
No policy violations detected.
""",
to: '[email protected]'
)
}
failure {
// Send failure notification
emailext (
subject: "Mend Scan FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Mend security scan failed for ${env.JOB_NAME}
Build: ${env.BUILD_URL}
Check the build logs for policy violation details.
""",
to: '[email protected]'
)
}
}
}
stage('Security Gate') {
steps {
script {
// Check for critical vulnerabilities
def scanReport = readJSON file: 'whitesource/scanResult.json'
def criticalVulns = scanReport.alerts.count { alert ->
alert.severity == 'CRITICAL' && alert.status != 'IGNORED'
}
if (criticalVulns > 0) {
error "Build failed: ${criticalVulns} critical vulnerabilities found"
}
// Check license compliance
def licenseViolations = scanReport.licenseViolations.size()
if (licenseViolations > 0) {
error "Build failed: ${licenseViolations} license violations found"
}
}
}
}
}
post {
always {
// Cleanup
sh 'rm -f /tmp/whitesource-agent.jar'
}
}
}

GitHub Actions Workflow

# .github/workflows/mend-scan.yml
name: Mend Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * 1'  # Weekly on Monday at 2 AM
jobs:
mend-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build project
run: mvn clean compile -DskipTests
- name: Download Mend Unified Agent
run: |
curl -L -o whitesource-agent.jar \
"https://github.com/whitesource/fs-agent-distribution/raw/master/standalone/whitesource-fs-agent-21.11.1.jar"
- name: Run Mend Scan
run: |
java -jar whitesource-agent.jar -c whitesource.config \
-apiKey ${{ secrets.MEND_ORG_TOKEN }} \
-product "GitHub-${{ github.repository }}" \
-project "${{ github.event.repository.name }}" \
-projectVersion "${{ github.sha }}" \
-d . \
-wss.url "https://saas.mend.io"
env:
MEND_ORG_TOKEN: ${{ secrets.MEND_ORG_TOKEN }}
- name: Upload Mend Report
uses: actions/upload-artifact@v3
with:
name: mend-security-report
path: |
whitesource/scanResult.json
whitesource/whitesource-*.html
retention-days: 30
- name: Check for Critical Vulnerabilities
run: |
python .github/scripts/check_mend_results.py
- name: Notify Security Channel
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: |
Mend scan failed for ${{ github.repository }}
Check run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
fields: repo,commit,author,action,eventName,ref,workflow
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_SECURITY_WEBHOOK }}

Advanced Mend Integration Framework

Java Service for Mend Operations

package com.company.mend;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@Service
public class MendService {
private final ObjectMapper objectMapper;
private final MendConfigManager configManager;
private final String apiBaseUrl;
private final String orgToken;
public MendService(ObjectMapper objectMapper, 
MendConfigManager configManager,
@Value("${mend.api.url}") String apiBaseUrl,
@Value("${mend.org.token}") String orgToken) {
this.objectMapper = objectMapper;
this.configManager = configManager;
this.apiBaseUrl = apiBaseUrl;
this.orgToken = orgToken;
}
public ScanResult performScan(ProjectContext context) throws MendException {
try {
// Configure for specific project
configManager.setProjectConfiguration(
context.getProjectName(), 
context.getProjectVersion()
);
// Run Mend scan
Process process = runUnifiedAgent(context.getProjectPath());
// Parse results
Path resultPath = context.getProjectPath().resolve("whitesource/scanResult.json");
return parseScanResult(resultPath);
} catch (Exception e) {
throw new MendException("Mend scan failed", e);
}
}
public List<Vulnerability> getProjectVulnerabilities(String projectToken) throws MendException {
try {
String endpoint = apiBaseUrl + "/api/v1.3";
Map<String, Object> request = Map.of(
"requestType", "getProjectVulnerabilities",
"userKey", orgToken,
"projectToken", projectToken
);
String response = sendApiRequest(endpoint, request);
return parseVulnerabilities(response);
} catch (Exception e) {
throw new MendException("Failed to fetch vulnerabilities", e);
}
}
public List<License> getProjectLicenses(String projectToken) throws MendException {
try {
String endpoint = apiBaseUrl + "/api/v1.3";
Map<String, Object> request = Map.of(
"requestType", "getProjectLicenses",
"userKey", orgToken,
"projectToken", projectToken
);
String response = sendApiRequest(endpoint, request);
return parseLicenses(response);
} catch (Exception e) {
throw new MendException("Failed to fetch licenses", e);
}
}
public boolean ignoreVulnerability(String vulnerabilityId, 
String projectToken, 
String comment) throws MendException {
try {
String endpoint = apiBaseUrl + "/api/v1.3";
Map<String, Object> request = Map.of(
"requestType", "ignoreVulnerability",
"userKey", orgToken,
"projectToken", projectToken,
"vulnerabilityId", vulnerabilityId,
"comment", comment
);
String response = sendApiRequest(endpoint, request);
return response.contains("\"success\":true");
} catch (Exception e) {
throw new MendException("Failed to ignore vulnerability", e);
}
}
private Process runUnifiedAgent(Path projectPath) throws IOException {
ProcessBuilder pb = new ProcessBuilder(
"java", "-jar", "whitesource-agent.jar",
"-c", "whitesource.config",
"-apiKey", orgToken,
"-d", projectPath.toString()
);
pb.directory(projectPath.toFile());
return pb.start();
}
private ScanResult parseScanResult(Path resultPath) throws IOException {
String jsonContent = Files.readString(resultPath);
return objectMapper.readValue(jsonContent, ScanResult.class);
}
private String sendApiRequest(String endpoint, Map<String, Object> request) throws IOException {
URL url = new URL(endpoint);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
String requestBody = objectMapper.writeValueAsString(request);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = requestBody.getBytes("utf-8");
os.write(input, 0, input.length);
}
try (BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8"))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
return response.toString();
}
}
private List<Vulnerability> parseVulnerabilities(String response) throws IOException {
Map<?, ?> responseMap = objectMapper.readValue(response, Map.class);
List<Map<?, ?>> vulnerabilities = (List<Map<?, ?>>) responseMap.get("vulnerabilities");
List<Vulnerability> result = new ArrayList<>();
for (Map<?, ?> vuln : vulnerabilities) {
result.add(new Vulnerability(
(String) vuln.get("name"),
(String) vuln.get("severity"),
(String) vuln.get("score"),
(String) vuln.get("publishDate"),
(String) vuln.get("type")
));
}
return result;
}
private List<License> parseLicenses(String response) throws IOException {
Map<?, ?> responseMap = objectMapper.readValue(response, Map.class);
List<Map<?, ?>> licenses = (List<Map<?, ?>>) responseMap.get("projectLicenses");
List<License> result = new ArrayList<>();
for (Map<?, ?> license : licenses) {
result.add(new License(
(String) license.get("name"),
(String) license.get("status"),
(String) license.get("riskLevel")
));
}
return result;
}
}
// Domain Models
class ProjectContext {
private final String projectName;
private final String projectVersion;
private final Path projectPath;
public ProjectContext(String projectName, String projectVersion, Path projectPath) {
this.projectName = projectName;
this.projectVersion = projectVersion;
this.projectPath = projectPath;
}
// Getters...
}
class ScanResult {
private final List<Library> libraries;
private final List<Vulnerability> vulnerabilities;
private final List<LicenseViolation> licenseViolations;
private final ScanSummary summary;
public ScanResult(List<Library> libraries, List<Vulnerability> vulnerabilities,
List<LicenseViolation> licenseViolations, ScanSummary summary) {
this.libraries = libraries;
this.vulnerabilities = vulnerabilities;
this.licenseViolations = licenseViolations;
this.summary = summary;
}
// Getters...
}
class Vulnerability {
private final String name;
private final String severity;
private final String score;
private final String publishDate;
private final String type;
public Vulnerability(String name, String severity, String score, 
String publishDate, String type) {
this.name = name;
this.severity = severity;
this.score = score;
this.publishDate = publishDate;
this.type = type;
}
// Getters...
}
class MendException extends Exception {
public MendException(String message) {
super(message);
}
public MendException(String message, Throwable cause) {
super(message, cause);
}
}

Policy Management and Automation

Automated Policy Enforcement

package com.company.mend.policy;
import com.company.mend.ScanResult;
import com.company.mend.Vulnerability;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
@Component
public class MendPolicyEnforcer {
private final List<SecurityPolicy> policies;
private final PolicyConfiguration config;
public MendPolicyEnforcer(PolicyConfiguration config) {
this.config = config;
this.policies = loadPolicies();
}
public PolicyValidationResult validateScanResult(ScanResult scanResult) {
List<PolicyViolation> violations = new ArrayList<>();
List<Vulnerability> criticalVulnerabilities = new ArrayList<>();
// Check vulnerability policies
for (Vulnerability vuln : scanResult.getVulnerabilities()) {
PolicyViolation violation = checkVulnerabilityPolicy(vuln);
if (violation != null) {
violations.add(violation);
}
if (isCriticalVulnerability(vuln)) {
criticalVulnerabilities.add(vuln);
}
}
// Check license policies
violations.addAll(checkLicensePolicies(scanResult.getLicenseViolations()));
return new PolicyValidationResult(violations, criticalVulnerabilities);
}
public boolean shouldFailBuild(PolicyValidationResult validation) {
if (config.isFailOnCritical() && !validation.getCriticalVulnerabilities().isEmpty()) {
return true;
}
if (config.isFailOnHighSeverity()) {
long highSeverityCount = validation.getViolations().stream()
.filter(v -> v.getSeverity() == Severity.HIGH)
.count();
return highSeverityCount > config.getMaxAllowedHighSeverity();
}
return !validation.getViolations().isEmpty() && config.isFailOnAnyViolation();
}
private PolicyViolation checkVulnerabilityPolicy(Vulnerability vulnerability) {
for (SecurityPolicy policy : policies) {
if (policy.matches(vulnerability)) {
return new PolicyViolation(
policy.getName(),
vulnerability,
policy.getSeverity(),
policy.getDescription()
);
}
}
return null;
}
private List<PolicyViolation> checkLicensePolicies(List<LicenseViolation> licenseViolations) {
List<PolicyViolation> violations = new ArrayList<>();
for (LicenseViolation licenseViolation : licenseViolations) {
if (isProhibitedLicense(licenseViolation.getLicense())) {
violations.add(new PolicyViolation(
"Prohibited License",
licenseViolation,
Severity.HIGH,
"License " + licenseViolation.getLicense() + " is prohibited"
));
}
}
return violations;
}
private boolean isCriticalVulnerability(Vulnerability vulnerability) {
return "CRITICAL".equalsIgnoreCase(vulnerability.getSeverity()) ||
(vulnerability.getScore() != null && 
Double.parseDouble(vulnerability.getScore()) >= 9.0);
}
private boolean isProhibitedLicense(String license) {
return config.getProhibitedLicenses().stream()
.anyMatch(prohibited -> prohibited.equalsIgnoreCase(license));
}
private List<SecurityPolicy> loadPolicies() {
List<SecurityPolicy> policies = new ArrayList<>();
// Critical vulnerability policy
policies.add(new SecurityPolicy(
"Critical Vulnerability Policy",
vuln -> "CRITICAL".equalsIgnoreCase(vuln.getSeverity()),
Severity.CRITICAL,
"Critical vulnerabilities are not allowed"
));
// High severity with no fix policy
policies.add(new SecurityPolicy(
"High Severity No Fix",
vuln -> "HIGH".equalsIgnoreCase(vuln.getSeverity()) && 
vuln.getType().contains("No Fix"),
Severity.HIGH,
"High severity vulnerabilities with no fix available"
));
// Specific CVE patterns
policies.add(new SecurityPolicy(
"Log4Shell Detection",
vuln -> vuln.getName().contains("CVE-2021-44228") ||
vuln.getName().contains("Log4Shell"),
Severity.CRITICAL,
"Log4Shell vulnerability detected"
));
return policies;
}
}
// Policy Configuration
@Component
@ConfigurationProperties(prefix = "mend.policy")
@Data
public class PolicyConfiguration {
private boolean failOnCritical = true;
private boolean failOnHighSeverity = true;
private boolean failOnAnyViolation = false;
private int maxAllowedHighSeverity = 0;
private List<String> prohibitedLicenses = Arrays.asList(
"GPL-3.0", "AGPL-3.0", "SSPL-1.0"
);
private List<String> allowedLicenses = Arrays.asList(
"Apache-2.0", "MIT", "BSD-3-Clause", "EPL-2.0"
);
}
class SecurityPolicy {
private final String name;
private final VulnerabilityPredicate predicate;
private final Severity severity;
private final String description;
public SecurityPolicy(String name, VulnerabilityPredicate predicate, 
Severity severity, String description) {
this.name = name;
this.predicate = predicate;
this.severity = severity;
this.description = description;
}
public boolean matches(Vulnerability vulnerability) {
return predicate.test(vulnerability);
}
// Getters...
}
@FunctionalInterface
interface VulnerabilityPredicate {
boolean test(Vulnerability vulnerability);
}
enum Severity {
CRITICAL, HIGH, MEDIUM, LOW, INFO
}
class PolicyValidationResult {
private final List<PolicyViolation> violations;
private final List<Vulnerability> criticalVulnerabilities;
public PolicyValidationResult(List<PolicyViolation> violations, 
List<Vulnerability> criticalVulnerabilities) {
this.violations = violations;
this.criticalVulnerabilities = criticalVulnerabilities;
}
// Getters...
}

Reporting and Dashboard Integration

Custom Report Generation

package com.company.mend.reporting;
import com.company.mend.ScanResult;
import com.company.mend.Vulnerability;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Component
public class MendReportGenerator {
private final ObjectMapper objectMapper;
public MendReportGenerator(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public void generateHtmlReport(ScanResult scanResult, Path outputPath) throws IOException {
String htmlContent = buildHtmlReport(scanResult);
Files.write(outputPath, htmlContent.getBytes());
}
public void generateJsonReport(ScanResult scanResult, Path outputPath) throws IOException {
Map<String, Object> report = buildJsonReport(scanResult);
objectMapper.writerWithDefaultPrettyPrinter()
.writeValue(outputPath.toFile(), report);
}
public void generateMarkdownReport(ScanResult scanResult, Path outputPath) throws IOException {
String markdown = buildMarkdownReport(scanResult);
Files.write(outputPath, markdown.getBytes());
}
public void generateSecurityDashboard(ScanResult scanResult, Path outputPath) throws IOException {
Map<String, Object> dashboardData = buildDashboardData(scanResult);
String htmlDashboard = buildDashboardHtml(dashboardData);
Files.write(outputPath, htmlDashboard.getBytes());
}
private String buildHtmlReport(ScanResult scanResult) {
return """
<!DOCTYPE html>
<html>
<head>
<title>Mend Security Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.critical { color: #d73a49; font-weight: bold; }
.high { color: #f66a0a; }
.medium { color: #ffd33d; }
.low { color: #0366d6; }
.vulnerability { border: 1px solid #e1e4e8; padding: 10px; margin: 10px 0; }
.summary { background: #f6f8fa; padding: 15px; border-radius: 5px; }
</style>
</head>
<body>
<h1>Mend Security Scan Report</h1>
<div class="summary">
<h2>Scan Summary</h2>
<p><strong>Generated:</strong> %s</p>
<p><strong>Total Dependencies:</strong> %d</p>
<p><strong>Total Vulnerabilities:</strong> %d</p>
<p><strong>Critical Vulnerabilities:</strong> <span class="critical">%d</span></p>
<p><strong>High Vulnerabilities:</strong> <span class="high">%d</span></p>
</div>
<h2>Vulnerabilities</h2>
%s
</body>
</html>
""".formatted(
LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
scanResult.getLibraries().size(),
scanResult.getVulnerabilities().size(),
countVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "CRITICAL"),
countVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "HIGH"),
buildVulnerabilitiesHtml(scanResult.getVulnerabilities())
);
}
private String buildVulnerabilitiesHtml(List<Vulnerability> vulnerabilities) {
return vulnerabilities.stream()
.sorted(Comparator.comparing(Vulnerability::getSeverity).reversed())
.map(vuln -> """
<div class="vulnerability">
<h3 class="%s">%s - %s</h3>
<p><strong>Score:</strong> %s</p>
<p><strong>Published:</strong> %s</p>
<p><strong>Type:</strong> %s</p>
</div>
""".formatted(
vuln.getSeverity().toLowerCase(),
vuln.getName(),
vuln.getSeverity(),
vuln.getScore(),
vuln.getPublishDate(),
vuln.getType()
))
.collect(Collectors.joining());
}
private String buildMarkdownReport(ScanResult scanResult) {
StringBuilder md = new StringBuilder();
md.append("# Mend Security Scan Report\n\n");
// Summary
md.append("## Scan Summary\n\n");
md.append("- **Generated**: ").append(LocalDateTime.now()).append("\n");
md.append("- **Total Dependencies**: ").append(scanResult.getLibraries().size()).append("\n");
md.append("- **Total Vulnerabilities**: ").append(scanResult.getVulnerabilities().size()).append("\n");
md.append("- **Critical Vulnerabilities**: ").append(countVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "CRITICAL")).append("\n");
md.append("- **High Vulnerabilities**: ").append(countVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "HIGH")).append("\n\n");
// Critical Vulnerabilities
List<Vulnerability> criticalVulns = getVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "CRITICAL");
if (!criticalVulns.isEmpty()) {
md.append("## 🔴 Critical Vulnerabilities\n\n");
for (Vulnerability vuln : criticalVulns) {
md.append("### ").append(vuln.getName()).append("\n");
md.append("- **Severity**: ").append(vuln.getSeverity()).append("\n");
md.append("- **Score**: ").append(vuln.getScore()).append("\n");
md.append("- **Type**: ").append(vuln.getType()).append("\n");
md.append("- **Published**: ").append(vuln.getPublishDate()).append("\n\n");
}
}
return md.toString();
}
private long countVulnerabilitiesBySeverity(List<Vulnerability> vulnerabilities, String severity) {
return vulnerabilities.stream()
.filter(v -> severity.equalsIgnoreCase(v.getSeverity()))
.count();
}
private List<Vulnerability> getVulnerabilitiesBySeverity(List<Vulnerability> vulnerabilities, String severity) {
return vulnerabilities.stream()
.filter(v -> severity.equalsIgnoreCase(v.getSeverity()))
.collect(Collectors.toList());
}
private Map<String, Object> buildJsonReport(ScanResult scanResult) {
return Map.of(
"scanSummary", Map.of(
"generated", LocalDateTime.now().toString(),
"totalDependencies", scanResult.getLibraries().size(),
"totalVulnerabilities", scanResult.getVulnerabilities().size(),
"vulnerabilityBreakdown", getVulnerabilityBreakdown(scanResult.getVulnerabilities())
),
"criticalVulnerabilities", getVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "CRITICAL"),
"highVulnerabilities", getVulnerabilitiesBySeverity(scanResult.getVulnerabilities(), "HIGH"),
"libraries", scanResult.getLibraries()
);
}
private Map<String, Long> getVulnerabilityBreakdown(List<Vulnerability> vulnerabilities) {
return vulnerabilities.stream()
.collect(Collectors.groupingBy(
Vulnerability::getSeverity,
Collectors.counting()
));
}
}

Best Practices and Configuration

Security Configuration

package com.company.mend.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
@ConfigurationProperties(prefix = "mend")
@Data
public class MendConfiguration {
// API Configuration
private String apiUrl = "https://saas.mend.io";
private String orgToken;
private String productName;
private int timeoutSeconds = 300;
// Scan Configuration
private boolean checkPolicies = true;
private boolean failOnError = true;
private boolean failOnPolicyViolation = true;
private boolean forceUpdate = true;
private List<String> includes = List.of("**/*.jar", "**/*.war", "**/*.ear");
private List<String> excludes = List.of("**/test-classes/**", "**/test/**");
// Reporting Configuration
private boolean generateHtmlReport = true;
private boolean generateJsonReport = true;
private String reportDirectory = "target/mend-reports";
// Alerting Configuration
private boolean alertOnCritical = true;
private boolean alertOnHighSeverity = true;
private List<String> alertEmails;
private String slackWebhookUrl;
// Proxy Configuration
private Proxy proxy;
@Data
public static class Proxy {
private String host;
private int port;
private String username;
private String password;
private boolean enabled = false;
}
public boolean isValid() {
return orgToken != null && !orgToken.trim().isEmpty() &&
productName != null && !productName.trim().isEmpty();
}
}

Integration Checklist

  1. Obtain Mend Organization Token from your Mend dashboard
  2. Configure build tools (Maven/Gradle) with Mend plugins
  3. Set up Unified Agent for comprehensive scanning
  4. Configure CI/CD pipelines for automated scanning
  5. Define security policies based on organizational requirements
  6. Set up reporting and alerting for security findings
  7. Establish remediation workflows for addressing vulnerabilities
  8. Monitor and tune scan configurations regularly

Key Benefits

  • Comprehensive vulnerability detection across all dependencies
  • License compliance management with policy enforcement
  • Automated scanning integrated into development workflows
  • Detailed reporting with actionable insights
  • Policy-based security gates in CI/CD pipelines

By implementing these Mend integration patterns, organizations can effectively manage open-source security risks while maintaining development velocity and compliance requirements.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper