diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index ba00d41..695a59f 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -11,6 +11,7 @@ on: - 'build.gradle' - 'settings.gradle' - 'gradle/**' + - 'dependency-suppression.xml' - 'gradlew' - 'gradlew.bat' - '.github/workflows/pages.yml' @@ -97,6 +98,8 @@ jobs: JMH_CSV_LINK='' JMH_TXT_LATEST_LINK='' JMH_CSV_LATEST_LINK='' + DEPENDENCY_CHECK_LINK='' + DEPENDENCY_CHECK_LATEST_LINK='' if [ -d "build/reports/jmh" ]; then cp -R build/reports/jmh "${RUN_DIR}/jmh" @@ -116,6 +119,16 @@ jobs: HAS_JMH="false" 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='
  • Dependency Vulnerability Report
  • ' + DEPENDENCY_CHECK_LATEST_LINK='
  • Dependency Vulnerability Report
  • ' + fi + fi + cat > "${RUN_DIR}/index.html" < @@ -139,6 +152,7 @@ jobs:
  • Test Report
  • PMD Report
  • Coverage Report
  • + ${DEPENDENCY_CHECK_LINK:-
  • Dependency Vulnerability Report: not available
  • }
  • Mutation Testing Report
  • $( [ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LINK:-
  • Benchmark Results (TXT): not available
  • }"; echo "${JMH_CSV_LINK:-
  • Benchmark Results (CSV): not available
  • }"; } \ @@ -185,6 +199,7 @@ jobs:
  • Test Report
  • PMD Report
  • Coverage Report
  • + ${DEPENDENCY_CHECK_LATEST_LINK:-
  • Dependency Vulnerability Report: not currently available
  • }
  • Mutation Testing Report
  • $( [ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LATEST_LINK:-
  • Benchmark Results (TXT): not available
  • }"; echo "${JMH_CSV_LATEST_LINK:-
  • Benchmark Results (CSV): not available
  • }"; } \ diff --git a/build.gradle b/build.gradle index 12eab0e..51156c0 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ plugins { id 'jacoco' id 'info.solidsoft.pitest' version '1.19.0' 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' } @@ -14,6 +15,11 @@ version = gitVersion(prefix:'release@') 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 { 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 { useJUnitPlatform() jvmArgs += "-javaagent:${configurations.mockitoAgent.singleFile}" @@ -83,6 +121,7 @@ tasks.named('jacocoTestReport', JacocoReport) { tasks.named('check') { dependsOn(tasks.named('jacocoTestReport')) + // no-default, only on-demand: dependsOn(tasks.named('dependencyCheckAnalyze')) } 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 { failOnError = false diff --git a/dependency-suppression.xml b/dependency-suppression.xml new file mode 100644 index 0000000..2b45b90 --- /dev/null +++ b/dependency-suppression.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file