JFrog Xray provides universal software composition analysis (SCA) that scans your Java artifacts for security vulnerabilities and license compliance issues across your software supply chain.
Setup and Configuration
1. Maven Dependencies and Plugins
<!-- pom.xml - Xray Integration -->
<properties>
<jfrog.xray.version>3.0.0</jfrog.xray.version>
<jfrog.maven.plugin.version>3.1.0</jfrog.maven.plugin.version>
</properties>
<build>
<plugins>
<!-- JFrog Maven Plugin -->
<plugin>
<groupId>org.jfrog.buildinfo</groupId>
<artifactId>artifactory-maven-plugin</artifactId>
<version>${jfrog.maven.plugin.version}</version>
<executions>
<execution>
<id>artifactory-publish</id>
<goals>
<goal>publish</goal>
</goals>
</execution>
</executions>
<configuration>
<artifactory>
<server>
<id>artifactory</id>
<url>${artifactory.url}</url>
<username>${artifactory.username}</username>
<password>${artifactory.password}</password>
</server>
<publisher>
<repoKey>libs-release-local</repoKey>
<snapshotRepoKey>libs-snapshot-local</snapshotRepoKey>
</publisher>
</artifactory>
</configuration>
</plugin>
<!-- OWASP Dependency Check (Complementary) -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.4.2</version>
<configuration>
<formats>HTML,JSON</formats>
<failBuildOnAnyVulnerability>false</failBuildOnAnyVulnerability>
<suppressionFiles>
<suppressionFile>${project.basedir}/security/xray-suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
</plugins>
</build>
<!-- Dependencies with various license types for testing -->
<dependencies>
<!-- Apache 2.0 License -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.0</version>
</dependency>
<!-- MIT License -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
<!-- GPL License (for compliance testing) -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.3.1.Final</version> <!-- LGPL -->
</dependency>
<!-- Dependencies with known vulnerabilities -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2. Gradle Configuration
// build.gradle
plugins {
id 'java'
id 'com.jfrog.artifactory' version '4.31.6'
id 'org.owasp.dependencycheck' version '8.4.2'
}
artifactory {
contextUrl = System.getenv('ARTIFACTORY_URL') ?: 'https://artifactory.example.com'
publish {
repository {
repoKey = 'libs-release-local'
username = System.getenv('ARTIFACTORY_USERNAME')
password = System.getenv('ARTIFACTORY_PASSWORD')
}
defaults {
publications('mavenJava')
}
}
}
dependencyCheck {
formats = ['HTML', 'JSON']
suppressionFile = 'security/xray-suppressions.xml'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0'
implementation 'com.google.guava:guava:32.1.2-jre'
implementation 'org.hibernate:hibernate-core:6.3.1.Final'
implementation 'commons-collections:commons-collections:3.2.1'
implementation 'log4j:log4j:1.2.17'
}
3. Environment Setup Script
#!/bin/bash
# setup-xray.sh
# Set Artifactory/Xray environment variables
export ARTIFACTORY_URL=https://artifactory.example.com
export ARTIFACTORY_USERNAME=admin
export ARTIFACTORY_PASSWORD=password
export XRAY_URL=https://xray.example.com
export XRAY_USERNAME=admin
export XRAY_PASSWORD=password
# Configure Maven settings
cat > ~/.m2/settings.xml << EOF
<settings>
<servers>
<server>
<id>artifactory</id>
<username>${ARTIFACTORY_USERNAME}</username>
<password>${ARTIFACTORY_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<id>artifactory</id>
<properties>
<artifactory.url>${ARTIFACTORY_URL}</artifactory.url>
<artifactory.username>${ARTIFACTORY_USERNAME}</artifactory.username>
<artifactory.password>${ARTIFACTORY_PASSWORD}</artifactory.password>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>artifactory</activeProfile>
</activeProfiles>
</settings>
EOF
echo "JFrog Xray environment configured"
Xray Configuration Files
1. Xray Policies Configuration
{
"name": "java-security-policy",
"description": "Security policy for Java applications",
"type": "security",
"rules": [
{
"name": "block-critical-vulnerabilities",
"criteria": {
"min_severity": "Critical",
"fix_version_only": false
},
"actions": {
"block_download": {
"unscanned": true,
"active": true
},
"block_release_bundle": true,
"fail_build": true
},
"priority": 1
},
{
"name": "block-high-vulnerabilities",
"criteria": {
"min_severity": "High",
"fix_version_only": false
},
"actions": {
"block_download": {
"unscanned": true,
"active": true
},
"fail_build": true
},
"priority": 2
},
{
"name": "warn-medium-vulnerabilities",
"criteria": {
"min_severity": "Medium",
"fix_version_only": false
},
"actions": {
"notify_deployer": true,
"notify_watch_recipients": true
},
"priority": 3
}
]
}
2. License Compliance Policy
{
"name": "java-license-policy",
"description": "License compliance policy for Java applications",
"type": "license",
"rules": [
{
"name": "block-prohibited-licenses",
"criteria": {
"allowed_licenses": [
"Apache-2.0",
"MIT",
"BSD-3-Clause",
"BSD-2-Clause",
"EPL-2.0",
"EPL-1.0"
],
"banned_licenses": [
"GPL-3.0",
"AGPL-3.0",
"GPL-2.0"
]
},
"actions": {
"block_download": true,
"fail_build": true,
"custom_severity": "High"
},
"priority": 1
},
{
"name": "review-copyleft-licenses",
"criteria": {
"allowed_licenses": [],
"banned_licenses": [],
"custom_licenses": [
{
"name": "LGPL-2.1",
"components": [
"hibernate-core:6.3.1.Final"
]
}
]
},
"actions": {
"notify_deployer": true,
"custom_severity": "Medium"
},
"priority": 2
}
]
}
3. Suppressions Configuration
<!-- security/xray-suppressions.xml --> <?xml version="1.0" encoding="UTF-8"?> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <!-- Suppress false positives --> <suppress> <notes><![CDATA[ False positive - CVE doesn't affect our usage pattern ]]></notes> <cve>CVE-2023-12345</cve> </suppress> <!-- Suppress by package with expiration --> <suppress until="2024-12-31"> <notes><![CDATA[ Legacy library with planned replacement in Q1 2024 ]]></notes> <packageUrl regex="true">^pkg:maven/commons\-collections/commons\-collections@.*$</packageUrl> </suppress> <!-- Suppress specific vulnerability in specific version --> <suppress> <notes><![CDATA[ Vulnerability mitigated by runtime configuration ]]></notes> <cve>CVE-2021-44228</cve> <packageUrl regex="true">^pkg:maven/log4j/log4j@1\.2\.17$</packageUrl> </suppress> </suppressions>
Integration Examples
1. GitHub Actions Workflow
# .github/workflows/xray-scan.yml
name: JFrog Xray Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 6 * * 1' # Weekly Monday at 6 AM
jobs:
security-scan:
name: Xray Security Scan
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build and publish to Artifactory
env:
ARTIFACTORY_URL: ${{ secrets.ARTIFACTORY_URL }}
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
run: |
mvn clean compile -DskipTests
mvn artifactory:publish -DskipTests
- name: Trigger Xray Scan
env:
XRAY_URL: ${{ secrets.XRAY_URL }}
XRAY_USERNAME: ${{ secrets.XRAY_USERNAME }}
XRAY_PASSWORD: ${{ secrets.XRAY_PASSWORD }}
run: |
# Trigger Xray scan via REST API
curl -X POST \
-u $XRAY_USERNAME:$XRAY_PASSWORD \
-H "Content-Type: application/json" \
-d '{
"component_id": "build://${{ github.repository }}/${{ github.sha }}",
"filters": {
"severity": ["Critical", "High", "Medium"]
}
}' \
"$XRAY_URL/api/v1/scan"
- name: OWASP Dependency Check
run: mvn org.owasp:dependency-check-maven:check
- name: Upload Security Reports
uses: actions/upload-artifact@v3
with:
name: security-reports
path: |
target/dependency-check-report.html
target/dependency-check-report.json
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: target/dependency-check-report.sarif
2. Jenkins Pipeline
// Jenkinsfile
pipeline {
agent any
environment {
ARTIFACTORY_URL = 'https://artifactory.example.com'
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
XRAY_URL = 'https://xray.example.com'
XRAY_CREDENTIALS = credentials('xray-credentials')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean compile -DskipTests'
}
}
stage('Dependency Scan') {
parallel {
stage('Xray Scan') {
steps {
script {
// Publish to Artifactory to trigger Xray
sh """
mvn artifactory:publish \
-Dartifactory.url=${ARTIFACTORY_URL} \
-Dartifactory.username=${ARTIFACTORY_CREDENTIALS_USR} \
-Dartifactory.password=${ARTIFACTORY_CREDENTIALS_PSW} \
-DskipTests
"""
// Wait for Xray scan to complete
def scanResult = waitForXrayScan()
if (scanResult.violations) {
error "Xray scan failed with ${scanResult.violations.size()} violations"
}
}
}
}
stage('OWASP Scan') {
steps {
sh 'mvn org.owasp:dependency-check-maven:check'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target',
reportFiles: 'dependency-check-report.html',
reportName: 'Dependency Check Report'
])
}
}
}
}
stage('Security Gate') {
steps {
script {
def xrayReport = readJSON file: 'target/xray-report.json'
def owaspReport = readJSON file: 'target/dependency-check-report.json'
// Evaluate security gates
if (hasCriticalVulnerabilities(xrayReport) || hasCriticalVulnerabilities(owaspReport)) {
error "Critical vulnerabilities detected - build blocked"
}
if (hasLicenseViolations(xrayReport)) {
error "License compliance violations detected - build blocked"
}
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'target/*.html, target/*.json'
}
failure {
emailext (
subject: "Security Scan Failed: ${env.JOB_NAME}",
body: "Security vulnerabilities detected in ${env.BUILD_URL}",
to: "[email protected]"
)
}
}
}
def waitForXrayScan() {
// Implementation to wait for Xray scan completion
return [violations: []]
}
def hasCriticalVulnerabilities(report) {
// Implementation to check for critical vulnerabilities
return false
}
def hasLicenseViolations(report) {
// Implementation to check for license violations
return false
}
3. GitLab CI Configuration
# .gitlab-ci.yml
stages:
- build
- security
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
cache:
paths:
- .m2/repository/
build:
stage: build
image: maven:3.8-openjdk-17
script:
- mvn clean compile -DskipTests
artifacts:
paths:
- target/classes/
xray-scan:
stage: security
image: maven:3.8-openjdk-17
script:
- |
mvn artifactory:publish \
-Dartifactory.url=$ARTIFACTORY_URL \
-Dartifactory.username=$ARTIFACTORY_USERNAME \
-Dartifactory.password=$ARTIFACTORY_PASSWORD \
-DskipTests
- |
# Trigger Xray scan and wait for results
curl -X POST \
-u $XRAY_USERNAME:$XRAY_PASSWORD \
-H "Content-Type: application/json" \
-d '{
"build_name": "$CI_PROJECT_NAME",
"build_number": "$CI_PIPELINE_ID"
}' \
"$XRAY_URL/api/v2/scans"
dependencies:
- build
only:
- main
- develop
- merge_requests
dependency-check:
stage: security
image: maven:3.8-openjdk-17
script:
- mvn org.owasp:dependency-check-maven:check
artifacts:
paths:
- target/dependency-check-report.html
reports:
sast: target/dependency-check-report.sarif
license-compliance:
stage: security
image: maven:3.8-openjdk-17
script:
- |
# Check license compliance using Xray API
curl -X GET \
-u $XRAY_USERNAME:$XRAY_PASSWORD \
"$XRAY_URL/api/v1/licenses?component=$CI_PROJECT_NAME" > license-report.json
- |
# Fail build if prohibited licenses found
if grep -q '"GPL-3.0"' license-report.json; then
echo "Prohibited license detected"
exit 1
fi
Java Security Services
1. Xray Integration Service
package com.example.security.xray;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.*;
@Service
public class XrayIntegrationService {
private final String XRAY_BASE_URL;
private final String ARTIFACTORY_BASE_URL;
private final RestTemplate restTemplate;
public XrayIntegrationService(
@Value("${xray.url}") String xrayUrl,
@Value("${artifactory.url}") String artifactoryUrl) {
this.XRAY_BASE_URL = xrayUrl;
this.ARTIFACTORY_BASE_URL = artifactoryUrl;
this.restTemplate = new RestTemplate();
}
public XrayScanResult scanArtifact(String artifactPath, String credentials) {
String url = XRAY_BASE_URL + "/api/v1/scan";
HttpHeaders headers = createHeaders(credentials);
ScanRequest request = new ScanRequest(artifactPath);
HttpEntity<ScanRequest> entity = new HttpEntity<>(request, headers);
ResponseEntity<XrayScanResult> response = restTemplate.exchange(
url, HttpMethod.POST, entity, XrayScanResult.class);
return response.getBody();
}
public List<Violation> getViolations(String buildName, String buildNumber, String credentials) {
String url = String.format("%s/api/v1/violations?build_name=%s&build_number=%s",
XRAY_BASE_URL, buildName, buildNumber);
HttpHeaders headers = createHeaders(credentials);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<ViolationResponse> response = restTemplate.exchange(
url, HttpMethod.GET, entity, ViolationResponse.class);
return response.getBody().getViolations();
}
public LicenseComplianceReport checkLicenseCompliance(String componentId, String credentials) {
String url = XRAY_BASE_URL + "/api/v1/licenses/" + componentId;
HttpHeaders headers = createHeaders(credentials);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<LicenseComplianceReport> response = restTemplate.exchange(
url, HttpMethod.GET, entity, LicenseComplianceReport.class);
return response.getBody();
}
public BuildSummary getBuildSummary(String buildName, String buildNumber, String credentials) {
String url = String.format("%s/api/v1/summary/build?build_name=%s&build_number=%s",
XRAY_BASE_URL, buildName, buildNumber);
HttpHeaders headers = createHeaders(credentials);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<BuildSummary> response = restTemplate.exchange(
url, HttpMethod.GET, entity, BuildSummary.class);
return response.getBody();
}
private HttpHeaders createHeaders(String credentials) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + credentials);
return headers;
}
// DTO classes for Xray API
public static class ScanRequest {
private String component_id;
public ScanRequest(String componentId) {
this.component_id = componentId;
}
public String getComponent_id() { return component_id; }
public void setComponent_id(String component_id) { this.component_id = component_id; }
}
public static class XrayScanResult {
private String scan_id;
private String status;
private int total_vulnerabilities;
private int total_components;
private List<Vulnerability> vulnerabilities;
// Getters and setters
public String getScan_id() { return scan_id; }
public void setScan_id(String scan_id) { this.scan_id = scan_id; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public int getTotal_vulnerabilities() { return total_vulnerabilities; }
public void setTotal_vulnerabilities(int total_vulnerabilities) {
this.total_vulnerabilities = total_vulnerabilities;
}
public int getTotal_components() { return total_components; }
public void setTotal_components(int total_components) {
this.total_components = total_components;
}
public List<Vulnerability> getVulnerabilities() { return vulnerabilities; }
public void setVulnerabilities(List<Vulnerability> vulnerabilities) {
this.vulnerabilities = vulnerabilities;
}
}
public static class Vulnerability {
private String cve;
private String severity;
private String component;
private String version;
private String fixed_versions;
private String summary;
// Getters and setters
public String getCve() { return cve; }
public void setCve(String cve) { this.cve = cve; }
public String getSeverity() { return severity; }
public void setSeverity(String severity) { this.severity = severity; }
public String getComponent() { return component; }
public void setComponent(String component) { this.component = component; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getFixed_versions() { return fixed_versions; }
public void setFixed_versions(String fixed_versions) { this.fixed_versions = fixed_versions; }
public String getSummary() { return summary; }
public void setSummary(String summary) { this.summary = summary; }
}
public static class ViolationResponse {
private List<Violation> violations;
public List<Violation> getViolations() { return violations; }
public void setViolations(List<Violation> violations) { this.violations = violations; }
}
public static class Violation {
private String issue_id;
private String summary;
private String severity;
private String component;
private String violation_type; // security or license
// Getters and setters
public String getIssue_id() { return issue_id; }
public void setIssue_id(String issue_id) { this.issue_id = issue_id; }
public String getSummary() { return summary; }
public void setSummary(String summary) { this.summary = summary; }
public String getSeverity() { return severity; }
public void setSeverity(String severity) { this.severity = severity; }
public String getComponent() { return component; }
public void setComponent(String component) { this.component = component; }
public String getViolation_type() { return violation_type; }
public void setViolation_type(String violation_type) { this.violation_type = violation_type; }
}
public static class LicenseComplianceReport {
private String component_id;
private List<LicenseFinding> license_findings;
private boolean compliant;
// Getters and setters
public String getComponent_id() { return component_id; }
public void setComponent_id(String component_id) { this.component_id = component_id; }
public List<LicenseFinding> getLicense_findings() { return license_findings; }
public void setLicense_findings(List<LicenseFinding> license_findings) {
this.license_findings = license_findings;
}
public boolean isCompliant() { return compliant; }
public void setCompliant(boolean compliant) { this.compliant = compliant; }
}
public static class LicenseFinding {
private String license;
private String component;
private String version;
private String status; // allowed, banned, requires_review
// Getters and setters
public String getLicense() { return license; }
public void setLicense(String license) { this.license = license; }
public String getComponent() { return component; }
public void setComponent(String component) { this.component = component; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
}
public static class BuildSummary {
private String build_name;
private String build_number;
private int total_violations;
private int security_violations;
private int license_violations;
// Getters and setters
public String getBuild_name() { return build_name; }
public void setBuild_name(String build_name) { this.build_name = build_name; }
public String getBuild_number() { return build_number; }
public void setBuild_number(String build_number) { this.build_number = build_number; }
public int getTotal_violations() { return total_violations; }
public void setTotal_violations(int total_violations) { this.total_violations = total_violations; }
public int getSecurity_violations() { return security_violations; }
public void setSecurity_violations(int security_violations) {
this.security_violations = security_violations;
}
public int getLicense_violations() { return license_violations; }
public void setLicense_violations(int license_violations) {
this.license_violations = license_violations;
}
}
}
2. Security Monitoring Service
package com.example.security.monitoring;
import com.example.security.xray.XrayIntegrationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SecurityMonitoringService {
private static final Logger logger = LoggerFactory.getLogger(SecurityMonitoringService.class);
private final XrayIntegrationService xrayService;
private final AlertService alertService;
public SecurityMonitoringService(XrayIntegrationService xrayService, AlertService alertService) {
this.xrayService = xrayService;
this.alertService = alertService;
}
@Scheduled(cron = "0 0 8 * * ?") // Daily at 8 AM
public void performDailySecurityScan() {
logger.info("Starting daily security scan");
try {
// Scan current build
String buildName = System.getenv("CI_PROJECT_NAME") ?? "local-build";
String buildNumber = System.getenv("CI_PIPELINE_ID") ?? "1";
String credentials = getXrayCredentials();
// Get violations for the build
List<XrayIntegrationService.Violation> violations =
xrayService.getViolations(buildName, buildNumber, credentials);
// Process violations
processViolations(violations);
// Check license compliance
XrayIntegrationService.LicenseComplianceReport licenseReport =
xrayService.checkLicenseCompliance(buildName, credentials);
processLicenseCompliance(licenseReport);
} catch (Exception e) {
logger.error("Daily security scan failed", e);
alertService.sendAlert("Security scan failure", e.getMessage());
}
}
@Scheduled(cron = "0 0 * * * ?") // Hourly
public void monitorCriticalVulnerabilities() {
logger.info("Monitoring for critical vulnerabilities");
try {
String credentials = getXrayCredentials();
// Get build summary
XrayIntegrationService.BuildSummary summary =
xrayService.getBuildSummary("production-build", "latest", credentials);
if (summary.getSecurity_violations() > 0) {
alertService.sendAlert(
"Critical vulnerabilities detected in production",
String.format("Found %d security violations", summary.getSecurity_violations())
);
}
} catch (Exception e) {
logger.error("Critical vulnerability monitoring failed", e);
}
}
private void processViolations(List<XrayIntegrationService.Violation> violations) {
int criticalCount = 0;
int highCount = 0;
for (XrayIntegrationService.Violation violation : violations) {
switch (violation.getSeverity()) {
case "Critical":
criticalCount++;
alertService.sendCriticalAlert(
"Critical security violation detected",
violation.getSummary()
);
break;
case "High":
highCount++;
alertService.sendAlert(
"High severity security violation",
violation.getSummary()
);
break;
}
}
if (criticalCount > 0 || highCount > 0) {
logger.warn("Found {} critical and {} high severity violations", criticalCount, highCount);
} else {
logger.info("No critical or high severity violations found");
}
}
private void processLicenseCompliance(XrayIntegrationService.LicenseComplianceReport report) {
if (!report.isCompliant()) {
alertService.sendAlert(
"License compliance issues detected",
"Found prohibited or restricted licenses"
);
for (XrayIntegrationService.LicenseFinding finding : report.getLicense_findings()) {
if ("banned".equals(finding.getStatus())) {
logger.error("Banned license found: {} in {}:{}",
finding.getLicense(), finding.getComponent(), finding.getVersion());
}
}
} else {
logger.info("License compliance check passed");
}
}
private String getXrayCredentials() {
// Implementation to get Xray credentials from secure storage
return System.getenv("XRAY_CREDENTIALS");
}
}
3. Dependency Management Service
package com.example.security.dependencies;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.*;
@Service
public class DependencyManagementService {
private static final Logger logger = LoggerFactory.getLogger(DependencyManagementService.class);
public DependencyReport analyzeDependencies() {
DependencyReport report = new DependencyReport();
try {
// Parse pom.xml to get dependencies
List<Dependency> dependencies = parsePomDependencies();
report.setDependencies(dependencies);
// Check for known vulnerable dependencies
List<VulnerableDependency> vulnerableDeps = findVulnerableDependencies(dependencies);
report.setVulnerableDependencies(vulnerableDeps);
// Check license compliance
List<LicenseViolation> licenseViolations = checkLicenseCompliance(dependencies);
report.setLicenseViolations(licenseViolations);
report.setAnalysisTimestamp(new Date());
report.setSuccessful(true);
} catch (Exception e) {
logger.error("Dependency analysis failed", e);
report.setSuccessful(false);
report.setErrorMessage(e.getMessage());
}
return report;
}
public boolean upgradeVulnerableDependency(String groupId, String artifactId, String version) {
try {
// Implementation to upgrade dependency in pom.xml
return updatePomDependency(groupId, artifactId, version);
} catch (Exception e) {
logger.error("Failed to upgrade dependency: {}:{}:{}", groupId, artifactId, version, e);
return false;
}
}
public List<DependencySuggestion> getUpgradeSuggestions() {
List<DependencySuggestion> suggestions = new ArrayList<>();
try {
// Parse current dependencies
List<Dependency> dependencies = parsePomDependencies();
// Check for available upgrades
for (Dependency dep : dependencies) {
String latestVersion = findLatestVersion(dep.getGroupId(), dep.getArtifactId());
if (latestVersion != null && !latestVersion.equals(dep.getVersion())) {
DependencySuggestion suggestion = new DependencySuggestion();
suggestion.setDependency(dep);
suggestion.setSuggestedVersion(latestVersion);
suggestion.setUpgradeType(determineUpgradeType(dep.getVersion(), latestVersion));
suggestions.add(suggestion);
}
}
} catch (Exception e) {
logger.error("Failed to get upgrade suggestions", e);
}
return suggestions;
}
private List<Dependency> parsePomDependencies() throws Exception {
List<Dependency> dependencies = new ArrayList<>();
// Implementation to parse pom.xml and extract dependencies
// This could use Maven model or direct XML parsing
// Example hardcoded for demonstration
dependencies.add(new Dependency("commons-collections", "commons-collections", "3.2.1"));
dependencies.add(new Dependency("log4j", "log4j", "1.2.17"));
dependencies.add(new Dependency("org.springframework.boot", "spring-boot-starter-web", "3.1.0"));
return dependencies;
}
private List<VulnerableDependency> findVulnerableDependencies(List<Dependency> dependencies) {
List<VulnerableDependency> vulnerable = new ArrayList<>();
// Implementation to check against vulnerability database
// This would integrate with Xray API or local database
for (Dependency dep : dependencies) {
if ("commons-collections".equals(dep.getGroupId()) && "3.2.1".equals(dep.getVersion())) {
VulnerableDependency vuln = new VulnerableDependency();
vuln.setDependency(dep);
vuln.setCve("CVE-2015-6420");
vuln.setSeverity("High");
vuln.setDescription("Deserialization vulnerability");
vulnerable.add(vuln);
}
}
return vulnerable;
}
private List<LicenseViolation> checkLicenseCompliance(List<Dependency> dependencies) {
List<LicenseViolation> violations = new ArrayList<>();
// Implementation to check license compliance
// This would integrate with Xray license policies
for (Dependency dep : dependencies) {
if ("log4j".equals(dep.getGroupId())) {
LicenseViolation violation = new LicenseViolation();
violation.setDependency(dep);
violation.setDetectedLicense("Apache-2.0");
violation.setAllowed(false);
violation.setReason("Prohibited license for commercial use");
violations.add(violation);
}
}
return violations;
}
private boolean updatePomDependency(String groupId, String artifactId, String version) {
// Implementation to update pom.xml
// This could use Maven model or direct XML manipulation
return true;
}
private String findLatestVersion(String groupId, String artifactId) {
// Implementation to find latest version from Maven Central or Artifactory
return "4.4"; // Example for commons-collections
}
private String determineUpgradeType(String currentVersion, String latestVersion) {
// Implementation to determine if it's major, minor, or patch upgrade
return "minor";
}
// DTO classes
public static class DependencyReport {
private boolean successful;
private String errorMessage;
private Date analysisTimestamp;
private List<Dependency> dependencies;
private List<VulnerableDependency> vulnerableDependencies;
private List<LicenseViolation> licenseViolations;
// Getters and setters
public boolean isSuccessful() { return successful; }
public void setSuccessful(boolean successful) { this.successful = successful; }
public String getErrorMessage() { return errorMessage; }
public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
public Date getAnalysisTimestamp() { return analysisTimestamp; }
public void setAnalysisTimestamp(Date analysisTimestamp) { this.analysisTimestamp = analysisTimestamp; }
public List<Dependency> getDependencies() { return dependencies; }
public void setDependencies(List<Dependency> dependencies) { this.dependencies = dependencies; }
public List<VulnerableDependency> getVulnerableDependencies() { return vulnerableDependencies; }
public void setVulnerableDependencies(List<VulnerableDependency> vulnerableDependencies) {
this.vulnerableDependencies = vulnerableDependencies;
}
public List<LicenseViolation> getLicenseViolations() { return licenseViolations; }
public void setLicenseViolations(List<LicenseViolation> licenseViolations) {
this.licenseViolations = licenseViolations;
}
}
public static class Dependency {
private String groupId;
private String artifactId;
private String version;
public Dependency(String groupId, String artifactId, String version) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
// Getters and setters
public String getGroupId() { return groupId; }
public void setGroupId(String groupId) { this.groupId = groupId; }
public String getArtifactId() { return artifactId; }
public void setArtifactId(String artifactId) { this.artifactId = artifactId; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
}
public static class VulnerableDependency {
private Dependency dependency;
private String cve;
private String severity;
private String description;
private String fixedVersion;
// Getters and setters
public Dependency getDependency() { return dependency; }
public void setDependency(Dependency dependency) { this.dependency = dependency; }
public String getCve() { return cve; }
public void setCve(String cve) { this.cve = cve; }
public String getSeverity() { return severity; }
public void setSeverity(String severity) { this.severity = severity; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getFixedVersion() { return fixedVersion; }
public void setFixedVersion(String fixedVersion) { this.fixedVersion = fixedVersion; }
}
public static class LicenseViolation {
private Dependency dependency;
private String detectedLicense;
private boolean allowed;
private String reason;
// Getters and setters
public Dependency getDependency() { return dependency; }
public void setDependency(Dependency dependency) { this.dependency = dependency; }
public String getDetectedLicense() { return detectedLicense; }
public void setDetectedLicense(String detectedLicense) { this.detectedLicense = detectedLicense; }
public boolean isAllowed() { return allowed; }
public void setAllowed(boolean allowed) { this.allowed = allowed; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
}
public static class DependencySuggestion {
private Dependency dependency;
private String suggestedVersion;
private String upgradeType; // major, minor, patch
// Getters and setters
public Dependency getDependency() { return dependency; }
public void setDependency(Dependency dependency) { this.dependency = dependency; }
public String getSuggestedVersion() { return suggestedVersion; }
public void setSuggestedVersion(String suggestedVersion) { this.suggestedVersion = suggestedVersion; }
public String getUpgradeType() { return upgradeType; }
public void setUpgradeType(String upgradeType) { this.upgradeType = upgradeType; }
}
}
4. Security Dashboard Controller
package com.example.security.controller;
import com.example.security.dependencies.DependencyManagementService;
import com.example.security.xray.XrayIntegrationService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/security")
@PreAuthorize("hasRole('SECURITY_TEAM')")
public class SecurityDashboardController {
private final XrayIntegrationService xrayService;
private final DependencyManagementService dependencyService;
public SecurityDashboardController(
XrayIntegrationService xrayService,
DependencyManagementService dependencyService) {
this.xrayService = xrayService;
this.dependencyService = dependencyService;
}
@GetMapping("/dependencies/report")
public DependencyManagementService.DependencyReport getDependencyReport() {
return dependencyService.analyzeDependencies();
}
@GetMapping("/dependencies/upgrade-suggestions")
public List<DependencyManagementService.DependencySuggestion> getUpgradeSuggestions() {
return dependencyService.getUpgradeSuggestions();
}
@PostMapping("/dependencies/upgrade")
public ResponseEntity<String> upgradeDependency(
@RequestParam String groupId,
@RequestParam String artifactId,
@RequestParam String version) {
boolean success = dependencyService.upgradeVulnerableDependency(groupId, artifactId, version);
if (success) {
return ResponseEntity.ok("Dependency upgraded successfully");
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to upgrade dependency");
}
}
@GetMapping("/xray/violations")
public List<XrayIntegrationService.Violation> getViolations(
@RequestParam String buildName,
@RequestParam String buildNumber) {
String credentials = getXrayCredentials();
return xrayService.getViolations(buildName, buildNumber, credentials);
}
@GetMapping("/xray/licenses")
public XrayIntegrationService.LicenseComplianceReport getLicenseCompliance(
@RequestParam String componentId) {
String credentials = getXrayCredentials();
return xrayService.checkLicenseCompliance(componentId, credentials);
}
@GetMapping("/xray/build-summary")
public XrayIntegrationService.BuildSummary getBuildSummary(
@RequestParam String buildName,
@RequestParam String buildNumber) {
String credentials = getXrayCredentials();
return xrayService.getBuildSummary(buildName, buildNumber, credentials);
}
private String getXrayCredentials() {
// Implementation to get credentials from secure storage
return System.getenv("XRAY_CREDENTIALS");
}
}
Best Practices
- Continuous Scanning:
- Integrate Xray into CI/CD pipelines
- Scan on every build and deployment
- Monitor for new vulnerabilities in production
- Policy Management:
- Define clear security and license policies
- Regular policy reviews and updates
- Automated policy enforcement
- Remediation Workflow:
- Prioritize critical vulnerabilities
- Automated dependency upgrades
- Manual review for complex upgrades
- Compliance Reporting:
- Regular compliance audits
- Automated reporting
- Executive dashboards
# Useful Xray CLI commands jf xray scan --build-name=my-app --build-number=1 jf xray audit --watches=my-watch jf xray bs --build-name=my-app --build-number=1
Conclusion
JFrog Xray for Java provides:
- Comprehensive vulnerability scanning across your software supply chain
- License compliance management with policy enforcement
- Deep recursive scanning of nested dependencies
- Integration with CI/CD pipelines and development workflows
- Advanced analytics and reporting capabilities
By implementing the patterns and configurations shown above, you can establish a robust software composition analysis practice, ensure license compliance, identify and remediate security vulnerabilities early, and maintain a secure software supply chain for your Java applications.
Secure Java Dependency Management, Vulnerability Scanning & Software Supply Chain Protection (SBOM, SCA, CI Security & License Compliance)
https://macronepal.com/blog/github-code-scanning-in-java-complete-guide/
Explains GitHub Code Scanning for Java using tools like CodeQL to automatically analyze source code and detect security vulnerabilities directly inside CI/CD pipelines before deployment.
https://macronepal.com/blog/license-compliance-in-java-comprehensive-guide/
Explains software license compliance in Java projects, ensuring dependencies follow legal requirements (MIT, Apache, GPL, etc.) and preventing license violations in enterprise software.
https://macronepal.com/blog/container-security-for-java-uncovering-vulnerabilities-with-grype/
Explains using Grype to scan Java container images and filesystems for known CVEs in OS packages and application dependencies to improve container security.
https://macronepal.com/blog/syft-sbom-generation-in-java-comprehensive-software-bill-of-materials-for-jvm-applications/
Explains using Syft to generate SBOMs (Software Bill of Materials) for Java applications, listing all dependencies, libraries, and components for supply chain transparency.
https://macronepal.com/blog/comprehensive-dependency-analysis-generating-and-scanning-sboms-with-trivy-for-java/
Explains using Trivy to generate SBOMs and scan Java dependencies and container images for vulnerabilities, integrating security checks into CI/CD pipelines.
https://macronepal.com/blog/dependabot-for-java-in-java/
Explains GitHub Dependabot for Java projects, which automatically detects vulnerable dependencies and creates pull requests to update them securely.
https://macronepal.com/blog/parasoft-jtest-in-java-comprehensive-guide-to-code-analysis-and-testing/
Explains Parasoft Jtest, a static analysis and testing tool for Java that helps detect bugs, security issues, and code quality problems early in development.
https://macronepal.com/blog/snyk-open-source-in-java-comprehensive-dependency-vulnerability-management-2/
Explains Snyk Open Source for Java, which continuously scans dependencies for vulnerabilities and provides automated fix suggestions and monitoring.
https://macronepal.com/blog/owasp-dependency-check-in-java-complete-vulnerability-scanning-guide/
Explains OWASP Dependency-Check, which scans Java dependencies against the National Vulnerability Database (NVD) to detect known security vulnerabilities.
https://macronepal.com/blog/securing-your-dependencies-a-java-developers-guide-to-whitesource-mend-bolt/
Explains Mend (WhiteSource) Bolt for Java, a dependency management and SCA tool that provides vulnerability detection, license compliance, and security policy enforcement in enterprise environments.