diff --git a/src/main/groovy/org/owasp/dependencycheck/gradle/extension/AdditionalCpe.groovy b/src/main/groovy/org/owasp/dependencycheck/gradle/extension/AdditionalCpe.groovy new file mode 100644 index 0000000..1537c48 --- /dev/null +++ b/src/main/groovy/org/owasp/dependencycheck/gradle/extension/AdditionalCpe.groovy @@ -0,0 +1,29 @@ +package org.owasp.dependencycheck.gradle.extension + +import org.gradle.api.Named + +/** + * Holder for the information regarding an additional CPE to be checked. + */ +@groovy.transform.CompileStatic +class AdditionalCpe implements Named { + + AdditionalCpe(String name) { + this.name = name; + } + + /** + * Name assigned to the CPE entry during configuration. + */ + String name; + + /** + * Description for the what the CPE represents. + */ + String description + + /** + * The CPE to be checked against the database. + */ + String cpe +} diff --git a/src/main/groovy/org/owasp/dependencycheck/gradle/extension/DependencyCheckExtension.groovy b/src/main/groovy/org/owasp/dependencycheck/gradle/extension/DependencyCheckExtension.groovy index b5ded0a..b44818f 100644 --- a/src/main/groovy/org/owasp/dependencycheck/gradle/extension/DependencyCheckExtension.groovy +++ b/src/main/groovy/org/owasp/dependencycheck/gradle/extension/DependencyCheckExtension.groovy @@ -18,6 +18,8 @@ package org.owasp.dependencycheck.gradle.extension +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import java.util.stream.Collectors @@ -210,12 +212,18 @@ class DependencyCheckExtension { * A set of files or folders to scan. */ List scanSet + /** + * Additional CPE to be analyzed. + */ + NamedDomainObjectContainer additionalCpes = + project.objects.domainObjectContainer(AdditionalCpe.class) /** * The configuration extension for cache settings. */ CacheExtension cache = new CacheExtension() + /** * Allows programmatic configuration of the proxy extension * @param configClosure the closure to configure the proxy extension @@ -278,4 +286,12 @@ class DependencyCheckExtension { def cache(Closure configClosure) { return project.configure(cache, configClosure) } + + /** + * Allows programmatic configuration of additional CPEs to be analyzed + * @param action the action used to add entries to additional CPEs container. + */ + def additionalCpes(Action> action) { + action.execute(additionalCpes) + } } diff --git a/src/main/groovy/org/owasp/dependencycheck/gradle/tasks/AbstractAnalyze.groovy b/src/main/groovy/org/owasp/dependencycheck/gradle/tasks/AbstractAnalyze.groovy index 4acff98..ef875b0 100644 --- a/src/main/groovy/org/owasp/dependencycheck/gradle/tasks/AbstractAnalyze.groovy +++ b/src/main/groovy/org/owasp/dependencycheck/gradle/tasks/AbstractAnalyze.groovy @@ -43,10 +43,12 @@ import org.owasp.dependencycheck.dependency.Confidence import org.owasp.dependencycheck.dependency.Dependency import org.owasp.dependencycheck.dependency.IncludedByReference import org.owasp.dependencycheck.dependency.Vulnerability +import org.owasp.dependencycheck.dependency.naming.CpeIdentifier import org.owasp.dependencycheck.exception.ExceptionCollection import org.owasp.dependencycheck.exception.ReportException import org.owasp.dependencycheck.gradle.service.SlackNotificationSenderService import org.owasp.dependencycheck.utils.SeverityUtil +import us.springett.parsers.cpe.CpeParser import static org.owasp.dependencycheck.dependency.EvidenceType.PRODUCT import static org.owasp.dependencycheck.dependency.EvidenceType.VENDOR @@ -458,6 +460,15 @@ abstract class AbstractAnalyze extends ConfiguredTask { } } } + + config.additionalCpes.each { + var dep = new Dependency(true); + dep.setDescription(it.description) + dep.addVulnerableSoftwareIdentifier(new CpeIdentifier(CpeParser.parse(it.cpe), Confidence.HIGHEST)) + dep.setFileName("") + dep.setActualFilePath("") + engine.addDependency(dep) + } } /** diff --git a/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckConfigurationSelectionIntegSpec.groovy b/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckConfigurationSelectionIntegSpec.groovy index 44bf4d6..6b3f308 100644 --- a/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckConfigurationSelectionIntegSpec.groovy +++ b/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckConfigurationSelectionIntegSpec.groovy @@ -46,6 +46,19 @@ class DependencyCheckConfigurationSelectionIntegSpec extends Specification { //result.output.contains('CVE-2015-5262') } + def "additional CPEs are scanned when present"() { + given: + copyBuildFileIntoProjectDir('scanAdditionalCpesConfiguration.gradle') + + when: + def result = executeTaskAndGetResult(ANALYZE_TASK, false) + + then: + result.task(":$ANALYZE_TASK").outcome == FAILED + result.output.contains('CVE-2015-6420') + result.output.contains('CVE-2016-3092') + } + def "custom configurations are scanned by default"() { given: copyBuildFileIntoProjectDir('scanCustomConfiguration.gradle') diff --git a/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckGradlePluginSpec.groovy b/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckGradlePluginSpec.groovy index df75e70..f569f3b 100644 --- a/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckGradlePluginSpec.groovy +++ b/src/test/groovy/org/owasp/dependencycheck/gradle/DependencyCheckGradlePluginSpec.groovy @@ -148,6 +148,23 @@ class DependencyCheckGradlePluginSpec extends Specification { suppressionFiles = ['./src/config/suppression1.xml', './src/config/suppression2.xml'] suppressionFileUser = 'suppressionFileUsername' suppressionFilePassword = 'suppressionFilePassword' + + additionalCpes { + additional1 { + description = "Additional1" + cpe = "cpe:2.3:a:aGroup1:aPackage1:123:*:*:*:*:*:*:*" + } + + additional2 { + description = "Additional2" + cpe = "cpe:2.3:a:aGroup2:aPackage2:123:*:*:*:*:*:*:*" + } + + additional3 { + description = "Additional3" + cpe = "cpe:2.3:a:aGroup3:aPackage3:123:*:*:*:*:*:*:*" + } + } } then: @@ -185,6 +202,10 @@ class DependencyCheckGradlePluginSpec extends Specification { project.dependencyCheck.analyzers.retirejs.filterNonVulnerable == true project.dependencyCheck.slack.enabled == true project.dependencyCheck.slack.webhookUrl == slackWebhookUrl + project.dependencyCheck.additionalCpes.size() == 3 + project.dependencyCheck.additionalCpes.getByName('additional1').description == 'Additional1' + project.dependencyCheck.additionalCpes.getByName('additional1').cpe == 'cpe:2.3:a:aGroup1:aPackage1:123:*:*:*:*:*:*:*' + } def 'scanConfigurations and skipConfigurations are mutually exclusive'() { diff --git a/src/test/resources/scanAdditionalCpesConfiguration.gradle b/src/test/resources/scanAdditionalCpesConfiguration.gradle new file mode 100644 index 0000000..a28d024 --- /dev/null +++ b/src/test/resources/scanAdditionalCpesConfiguration.gradle @@ -0,0 +1,26 @@ +plugins { + id 'org.owasp.dependencycheck' + id 'java' +} + +sourceCompatibility = 1.5 +version = '1.0' + +repositories { + mavenLocal() + mavenCentral() +} + +dependencyCheck { + failBuildOnCVSS = 0 + additionalCpes { + commonsCollections { + description = "Commons Collections 3.2" + cpe = "cpe:2.3:a:apache:commons_collections:3.2:*:*:*:*:*:*:*" + } + commonsFileUpload { + description = "Commons File Upload 1.3.1" + cpe = "cpe:2.3:a:apache:commons_fileupload:1.3.1:*:*:*:*:*:*:*" + } + } +}