Skip to content

Instantly share code, notes, and snippets.

@milo-minderbinder
Last active March 2, 2023 16:53
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save milo-minderbinder/1e1ed911c5b2264dc659578f1baaef16 to your computer and use it in GitHub Desktop.
Save milo-minderbinder/1e1ed911c5b2264dc659578f1baaef16 to your computer and use it in GitHub Desktop.
/**
* MIT License
*
* Copyright (c) 2018 Chris Passarello <www.insecurity.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
* This init script modifies the Gradle project by applying the DependencyCheck plugin to all projects.
*
* The plugin adds the `dependencyCheckAnalyze` task, which will scan the project and its dependencies
* for known vulnerabilities (see: https://github.com/jeremylong/DependencyCheck).
*
* To add it dynamically with a Gradle build command, add the `-I` option with the path to this file, e.g.:
* > gradle -I /path/to/dependencyCheck.gradle dependencyCheckAnalyze
*
* Alternatively, to add the plugin automatically to all Gradle projects transparently, place this script
* in the `$GRADLE_HOME/init.d` or `$USER_HOME/.gradle/init.d` directory.
*
* Sane defaults are set for several dependencyCheck extension properties
* (https://jeremylong.github.io/DependencyCheck/dependency-check-gradle/configuration.html), which may be overridden in
* the build script or via project properties, the latter having higher precedence. This allows the user to overwrite
* any dependencyCheck options configured in the build.gradle or other scripts, by setting
* the option in the {@code $GRADLE_HOME/gradle.properties}, for example, or by passing the option via the command line
* with:
* <br/><pre>>gradle dependencyCheckAnalyze -PdependencyCheck.failBuildOnCVSS=5</pre>
*
* Below is the full list of dependencyCheck options configurable as project properties and their default values:
* dependencyCheck.analyzers.experimentalEnabled = true
* dependencyCheck.analyzers.nuspecEnabled = false
* dependencyCheck.analyzers.nugetconfEnabled = false
* dependencyCheck.analyzers.assemblyEnabled = false
* dependencyCheck.analyzers.pathToBundleAudit = '/usr/local/bin/bundle-audit'
* dependencyCheck.cve.urlModified = 'https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-modified.json.gz'
* dependencyCheck.cve.urlBase = 'https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%d.json.gz'
* dependencyCheck.data.directory = null
* dependencyCheck.hintsFile = "${rootDir}/dependency-check-hints.xml" (if it exists)
* dependencyCheck.suppressionFiles = ["${rootDir}/dependency-check-hints.xml"] (if it exists)
* dependencyCheck.failBuildOnCVSS = 7.0
* dependencyCheck.format = 'ALL'
* dependencyCheck.outputDirectory = "${buildDir}/reports"
* dependencyCheck.skip = false
* dependencyCheck.skipConfigurations = []
* dependencyCheck.scanConfigurations = []
*
*/
import static org.owasp.dependencycheck.reporting.ReportGenerator.Format
void log(LogLevel level, String message, Object... objs) {
message = "(${level}) ${buildscript.sourceFile.name} - ${message}"
logger.log(level, message, *objs)
}
void log(String message, Object... objs) {
log(LogLevel.INFO, message, *objs)
}
log('applying')
/**
* Extract the named nested property from the given object.
* <p/>
* For example, if an object {@code foo} has a property {@code bar} which is also an object with its own property,
* {@code baz}, it can be accessed by calling <pre>extractProperty(foo, bar.baz)</pre>.
*
* @param obj the object to extract the property value from
* @param name the name of the (optionally) nested property to extract
* @return
*/
def extractProperty(obj, String name) {
name.tokenize('.').inject(obj) { a, v -> a."$v" }
}
/**
* Check the project for the named dependencyCheck option, giving precedence to project properties over the standard
* dependencyCheck configuration closure where the option is specified in both places, and using fallback as the default.
* <p/>
* This allows us to overwrite any dependencyCheck options configured in the build.gradle or other scripts, by setting
* the option in the {@code $GRADLE_HOME/gradle.properties}, for example, or by passing the option via the command line
* with:
* <br/><pre>>./gradlew dependencyCheckAnalyze -Dorg.gradle.project.dependencyCheck.failBuildOnCVSS=5</pre>
*
* @param project the Gradle project
* @param name the name of the option to retrieve
* @param fallback fallback value to return if the named option is not set in either the properties or the plugin's
* configuration closure
* @param pluginDefault the default value set by the plugin
* @return the value of the named option, if set in build properties or using the standard plugin configuration closure
* (in that order of precedence), otherwise {@code fallback}
*/
def getOption(Project project, String name, fallback, pluginDefault = null) {
def option = fallback
if (project.hasProperty("dependencyCheck.${name}")) {
option = project.property("dependencyCheck.${name}")
log("Set dependencyCheck.${name}=${option} via build properties")
} // Note: string coercion below is necessary since, for some reason, ReportGenerator.Format Enum comparison won't work
else if ("${extractProperty(project.dependencyCheck, name)}" != "${pluginDefault}") {
option = extractProperty(project.dependencyCheck, name)
log("Set dependencyCheck.${name}=${option} via config closure")
} else
log("Set dependencyCheck.${name}=${option} via init script fallback")
option
}
initscript {
ext {
dependencyCheckVersion = gradle.gradleVersion.startsWith('2.') ? '3.2.1' : '8.1.2'
}
// DepedencyCheck plugin is in Gradle Plugins Portal, but check if Gradle Plugins Portal is added already so we don't
// interfere with the number of repos added or their order.
boolean hasGradlePluginsPortal = repositories.withType(MavenArtifactRepository).find { MavenArtifactRepository r ->
r.url.toString().equals('https://plugins.gradle.org/m2/')
}
if (!hasGradlePluginsPortal) {
logger.log(LogLevel.INFO, "(INFO) ${buildscript.sourceFile.name} - Adding Gradle Plugin Portal to initscript repositories")
repositories {
gradlePluginPortal()
}
}
dependencies {
classpath "org.owasp:dependency-check-gradle:${dependencyCheckVersion}"
}
}
gradle.rootProject {
buildscript {
// DepedencyCheck plugin is in Gradle Plugins Portal, but check if Gradle Plugins Portal is added already so we don't
// interfere with the number of repos added or their order.
boolean hasGradlePluginsPortal = repositories.withType(MavenArtifactRepository).find { MavenArtifactRepository r ->
r.url.toString().equals('https://plugins.gradle.org/m2/')
}
if (!hasGradlePluginsPortal) {
log('Adding Gradle Plugin Portal to buildscript repositories')
repositories {
maven { url 'https://plugins.gradle.org/m2/' }
}
}
dependencies {
classpath "org.owasp:dependency-check-gradle:${dependencyCheckVersion}"
}
}
}
gradle.allprojects {
afterEvaluate { p ->
p.apply plugin: 'org.owasp.dependencycheck'
if (p.tasks.findByName('check')) {
p.check.dependsOn dependencyCheckAnalyze // Run DependencyCheck with other test/check tasks
}
p.dependencyCheckAnalyze {
doFirst {
dependencyCheck {
analyzers {
// required for NodeJS, PHP, Python, etc. (i.e. non-Java)
experimentalEnabled = getOption(p, 'analyzers.experimentalEnabled', true)
nuspecEnabled = getOption(p, 'analyzers.nuspecEnabled', false)
nugetconfEnabled = getOption(p, 'analyzers.nugetconfEnabled', false)
assemblyEnabled = getOption(p, 'analyzers.assemblyEnabled', false)
pathToBundleAudit = getOption(p, 'analyzers.pathToBundleAudit', '/usr/local/bin/bundle-audit')
//retiredEnabled = getOption(p, 'analyzers.retiredEnabled', true)
}
cve {
urlModified = getOption(p, 'cve.urlModified', 'https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-modified.json.gz')
urlBase = getOption(p, 'cve.urlBase', 'https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%d.json.gz')
}
data {
directory = getOption(p, 'data.directory', null)
}
// See hints file info here: https://jeremylong.github.io/DependencyCheck/general/hints.html
File defaultHintsFile = file(new File(project.rootDir, 'dependency-check-hints.xml'))
defaultHintsFile = defaultHintsFile.isFile() ? defaultHintsFile : null
hintsFile = getOption(p, 'hintsFile', defaultHintsFile)
// See suppression file info here: https://jeremylong.github.io/DependencyCheck/general/suppression.html
File defaultSuppressionFile = file(new File(project.rootDir, 'dependency-check-suppressions.xml'))
defaultSuppressionFile = defaultSuppressionFile.isFile() ? defaultSuppressionFile : null
List<String> allSuppressionFiles = p.dependencyCheck.suppressionFiles ?: []
def extractedVal = extractProperty(p.dependencyCheck, 'suppressionFile')
if (extractedVal)
allSuppressionFiles.add(extractedVal)
if (p.hasProperty('dependencyCheck.suppressionFiles')) {
allSuppressionFiles.addAll(p.property('dependencyCheck.suppressionFiles').split(',').collect {
it.trim()
})
}
if (p.hasProperty('dependencyCheck.suppressionFile'))
allSuppressionFiles.add(p.property('dependencyCheck.suppressionFile'))
if (defaultSuppressionFile)
allSuppressionFiles.add(defaultSuppressionFile.toString())
allSuppressionFiles = allSuppressionFiles.findAll {
file(it).isFile()
}.unique()
log("Set dependencyCheck.suppressionFiles=${allSuppressionFiles} via multiple sources")
suppressionFiles = allSuppressionFiles //as ArrayList<String>
suppressionFile = getOption(p, 'suppressionFile', defaultSuppressionFile)
// Non-zero exit code when high severity vulnerability is found
failBuildOnCVSS = getOption(p, 'failBuildOnCVSS', 7.0, 11.0) as Float
format = getOption(p, 'format', Format.ALL, Format.HTML) as String
outputDirectory = getOption(p, 'outputDirectory', "${p.buildDir}/reports", "${p.buildDir}/reports")
def coerceToList = { o ->
if (o instanceof String)
return o.split(',').collect { it.trim() }
return o
}
def coerceToBoolean = { o ->
if (o instanceof String)
return Boolean.parseBoolean(o)
return o
}
skip = coerceToBoolean(getOption(p, 'skip', false, false))
skipConfigurations = coerceToList(getOption(p, 'skipConfigurations', [], []))
scanConfigurations = coerceToList(getOption(p, 'scanConfigurations', [], []))
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment