build: add on-demand dependency vulnerability scanning support
build: add OWASP Dependency-Check Gradle integration with NVD API key support build: add conditional dependency suppression file support with unused-rule enforcement build: remove dependency scanning from default check lifecycle and regular CI builds build: keep gh-pages dependency report publication logic passive when report is absent
This commit is contained in:
15
.github/workflows/pages.yml
vendored
15
.github/workflows/pages.yml
vendored
@@ -11,6 +11,7 @@ on:
|
|||||||
- 'build.gradle'
|
- 'build.gradle'
|
||||||
- 'settings.gradle'
|
- 'settings.gradle'
|
||||||
- 'gradle/**'
|
- 'gradle/**'
|
||||||
|
- 'dependency-suppression.xml'
|
||||||
- 'gradlew'
|
- 'gradlew'
|
||||||
- 'gradlew.bat'
|
- 'gradlew.bat'
|
||||||
- '.github/workflows/pages.yml'
|
- '.github/workflows/pages.yml'
|
||||||
@@ -97,6 +98,8 @@ jobs:
|
|||||||
JMH_CSV_LINK=''
|
JMH_CSV_LINK=''
|
||||||
JMH_TXT_LATEST_LINK=''
|
JMH_TXT_LATEST_LINK=''
|
||||||
JMH_CSV_LATEST_LINK=''
|
JMH_CSV_LATEST_LINK=''
|
||||||
|
DEPENDENCY_CHECK_LINK=''
|
||||||
|
DEPENDENCY_CHECK_LATEST_LINK=''
|
||||||
|
|
||||||
if [ -d "build/reports/jmh" ]; then
|
if [ -d "build/reports/jmh" ]; then
|
||||||
cp -R build/reports/jmh "${RUN_DIR}/jmh"
|
cp -R build/reports/jmh "${RUN_DIR}/jmh"
|
||||||
@@ -116,6 +119,16 @@ jobs:
|
|||||||
HAS_JMH="false"
|
HAS_JMH="false"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -d "build/reports/dependency-check" ]; then
|
||||||
|
cp -R build/reports/dependency-check "${RUN_DIR}/dependency-check"
|
||||||
|
cp -R build/reports/dependency-check "${LATEST_DIR}/dependency-check"
|
||||||
|
|
||||||
|
if [ -f "${RUN_DIR}/dependency-check/dependency-check-report.html" ]; then
|
||||||
|
DEPENDENCY_CHECK_LINK='<li><a href="./dependency-check/dependency-check-report.html">Dependency Vulnerability Report</a></li>'
|
||||||
|
DEPENDENCY_CHECK_LATEST_LINK='<li><a href="./builds/latest/dependency-check/dependency-check-report.html">Dependency Vulnerability Report</a></li>'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
cat > "${RUN_DIR}/index.html" <<EOF
|
cat > "${RUN_DIR}/index.html" <<EOF
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -139,6 +152,7 @@ jobs:
|
|||||||
<li><a href="./test/">Test Report</a></li>
|
<li><a href="./test/">Test Report</a></li>
|
||||||
<li><a href="./pmd/main.html">PMD Report</a></li>
|
<li><a href="./pmd/main.html">PMD Report</a></li>
|
||||||
<li><a href="./coverage/">Coverage Report</a></li>
|
<li><a href="./coverage/">Coverage Report</a></li>
|
||||||
|
${DEPENDENCY_CHECK_LINK:-<li>Dependency Vulnerability Report: not available</li>}
|
||||||
<li><a href="./pitest/">Mutation Testing Report</a></li>
|
<li><a href="./pitest/">Mutation Testing Report</a></li>
|
||||||
$(
|
$(
|
||||||
[ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LINK:-<li>Benchmark Results (TXT): not available</li>}"; echo "${JMH_CSV_LINK:-<li>Benchmark Results (CSV): not available</li>}"; } \
|
[ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LINK:-<li>Benchmark Results (TXT): not available</li>}"; echo "${JMH_CSV_LINK:-<li>Benchmark Results (CSV): not available</li>}"; } \
|
||||||
@@ -185,6 +199,7 @@ jobs:
|
|||||||
<li><a href="./builds/latest/test/">Test Report</a></li>
|
<li><a href="./builds/latest/test/">Test Report</a></li>
|
||||||
<li><a href="./builds/latest/pmd/main.html">PMD Report</a></li>
|
<li><a href="./builds/latest/pmd/main.html">PMD Report</a></li>
|
||||||
<li><a href="./builds/latest/coverage/">Coverage Report</a></li>
|
<li><a href="./builds/latest/coverage/">Coverage Report</a></li>
|
||||||
|
${DEPENDENCY_CHECK_LATEST_LINK:-<li>Dependency Vulnerability Report: not currently available</li>}
|
||||||
<li><a href="./builds/latest/pitest/">Mutation Testing Report</a></li>
|
<li><a href="./builds/latest/pitest/">Mutation Testing Report</a></li>
|
||||||
$(
|
$(
|
||||||
[ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LATEST_LINK:-<li>Benchmark Results (TXT): not available</li>}"; echo "${JMH_CSV_LATEST_LINK:-<li>Benchmark Results (CSV): not available</li>}"; } \
|
[ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LATEST_LINK:-<li>Benchmark Results (TXT): not available</li>}"; echo "${JMH_CSV_LATEST_LINK:-<li>Benchmark Results (CSV): not available</li>}"; } \
|
||||||
|
|||||||
49
build.gradle
49
build.gradle
@@ -6,6 +6,7 @@ plugins {
|
|||||||
id 'jacoco'
|
id 'jacoco'
|
||||||
id 'info.solidsoft.pitest' version '1.19.0'
|
id 'info.solidsoft.pitest' version '1.19.0'
|
||||||
id 'me.champeau.jmh' version '0.7.2'
|
id 'me.champeau.jmh' version '0.7.2'
|
||||||
|
id 'org.owasp.dependencycheck' version '12.2.1'
|
||||||
id 'com.palantir.git-version' version '4.0.0'
|
id 'com.palantir.git-version' version '4.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,6 +15,11 @@ version = gitVersion(prefix:'release@')
|
|||||||
|
|
||||||
def benchmarkReportsDirectory = layout.buildDirectory.dir('reports/jmh')
|
def benchmarkReportsDirectory = layout.buildDirectory.dir('reports/jmh')
|
||||||
|
|
||||||
|
def nvdApiKey = providers.gradleProperty('nvdApiKey')
|
||||||
|
.orElse(providers.environmentVariable('NVD_API_KEY'))
|
||||||
|
.orNull
|
||||||
|
def dependencyCheckSuppressionFile = rootProject.file('dependency-suppression.xml')
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
mockitoAgent
|
mockitoAgent
|
||||||
}
|
}
|
||||||
@@ -52,6 +58,38 @@ dependencies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencyCheck {
|
||||||
|
failBuildOnCVSS = 7.0
|
||||||
|
failOnError = true
|
||||||
|
autoUpdate = true
|
||||||
|
formats = ['HTML', 'JSON']
|
||||||
|
outputDirectory = layout.buildDirectory.dir('reports/dependency-check').get().asFile.absolutePath
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep the scan focused on actual Java dependency inputs used by this project.
|
||||||
|
* testRuntimeClasspath is included intentionally because the current external
|
||||||
|
* dependency surface is primarily test-scoped.
|
||||||
|
*/
|
||||||
|
scanConfigurations = ['runtimeClasspath', 'testRuntimeClasspath', 'mockitoAgent']
|
||||||
|
skipTestGroups = false
|
||||||
|
|
||||||
|
analyzers {
|
||||||
|
experimentalEnabled = false
|
||||||
|
centralEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
nvd {
|
||||||
|
apiKey = nvdApiKey
|
||||||
|
delay = nvdApiKey != null ? 3500 : 8000
|
||||||
|
validForHours = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dependencyCheckSuppressionFile.exists()) {
|
||||||
|
suppressionFile = dependencyCheckSuppressionFile.absolutePath
|
||||||
|
failBuildOnUnusedSuppressionRule = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
tasks.withType(Test).configureEach {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
jvmArgs += "-javaagent:${configurations.mockitoAgent.singleFile}"
|
jvmArgs += "-javaagent:${configurations.mockitoAgent.singleFile}"
|
||||||
@@ -83,6 +121,7 @@ tasks.named('jacocoTestReport', JacocoReport) {
|
|||||||
|
|
||||||
tasks.named('check') {
|
tasks.named('check') {
|
||||||
dependsOn(tasks.named('jacocoTestReport'))
|
dependsOn(tasks.named('jacocoTestReport'))
|
||||||
|
// no-default, only on-demand: dependsOn(tasks.named('dependencyCheckAnalyze'))
|
||||||
}
|
}
|
||||||
|
|
||||||
pitest {
|
pitest {
|
||||||
@@ -149,6 +188,16 @@ tasks.register('regressionArtifactGenerator', JavaExec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register('printDependencyCheckNvdConfig') {
|
||||||
|
doLast {
|
||||||
|
System.out.println("NVD API key present: " + (nvdApiKey != null && !nvdApiKey.isBlank()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('dependencyCheckAnalyze') {
|
||||||
|
dependsOn(tasks.named('printDependencyCheckNvdConfig'))
|
||||||
|
}
|
||||||
|
|
||||||
javadoc {
|
javadoc {
|
||||||
failOnError = false
|
failOnError = false
|
||||||
|
|
||||||
|
|||||||
18
dependency-suppression.xml
Normal file
18
dependency-suppression.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
|
||||||
|
<!-- Add only reviewed, justified false positives. -->
|
||||||
|
|
||||||
|
<!-- Example:
|
||||||
|
<suppress>
|
||||||
|
<notes><![CDATA[
|
||||||
|
False positive review:
|
||||||
|
- reason: Example package was matched to wrong CPE
|
||||||
|
- reviewed-by: Security Team
|
||||||
|
- reviewed-on: 2026-04-14
|
||||||
|
- remove-when: After upgrading the plugin or dependency metadata improves
|
||||||
|
]]></notes>
|
||||||
|
<gav regex="true">^org\.example:acme-lib:.*$</gav>
|
||||||
|
<cve>CVE-2026-9999</cve>
|
||||||
|
</suppress>
|
||||||
|
-->
|
||||||
|
</suppressions>
|
||||||
Reference in New Issue
Block a user