HEX
Server: Apache
System: Linux webd004.cluster130.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User: frenchy (106757)
PHP: 7.4.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/frenchy/www/french-american.org/current/node_modules/snyk-gradle-plugin/lib/init.gradle
import groovy.json.JsonOutput
import java.util.regex.Pattern
import java.util.regex.Matcher

// Snyk dependency resolution script for Gradle.
// Tested on Gradle versions from 2.14 to 5.4.1

// This script does the following: for all the projects in the build file,
// generate a merged configuration of all the available configurations,
// and then list the dependencies as a tree.

// It's the responsibility of the caller to pick the project(s) they are
// interested in from the results.

// CLI usages:
// gradle -q -I init.gradle snykResolvedDepsJson
// gradle -q -I init.gradle snykResolvedDepsJson -Pconfiguration=specificConf -PonlySubProject=sub-project
// gradle -q -I init.gradle snykResolvedDepsJson -Pconfiguration=confNameRegex -PconfAttr=buildtype:debug,usage:java-runtime

// (-q to have clean output, -P supplies args as per https://stackoverflow.com/a/48370451)

// confAttr parameter (supported only in Gradle 3+) is used to perform attribute-based dependency variant matching
// (important for Android: https://developer.android.com/studio/build/dependencies#variant_aware)
// Its value is a comma-separated list of key:value pairs. The "key" is a case-insensitive substring
// of the class name of the attribute (e.g. "buildtype" would match com.android.build.api.attributes.BuildTypeAttr),
// the value should be a case-insensitive stringified value of the attribute

// Output format:
//
// Since Gradle is chatty and often prints a "Welcome" banner even with -q option,
// the only output lines that matter are:
// - prefixed "SNYKECHO ": should be immediately printed as debug information by the caller
// - prefixed "JSONDEPS ": JSON representation of the dependencies trees for all projects in the following format

// interface JsonDepsScriptResult {
//   defaultProject: string;
//   projects: ProjectsDict;
//   allSubProjectNames: string[];
// }
// interface ProjectsDict {
//   [project: string]: GradleProjectInfo;
// }

// interface GradleProjectInfo {
//   depDict: DepDict;
//   targetFile: string;
// }
// export interface DepDict {
//   [name: string]: DepTree;
// }
// interface DepTree {
//   name: string;
//   version: string;
//   dependencies?: DepDict;
// }

// We are attaching this task to every project, as this is the only reliable way to run it
// when we start with a subproject build.gradle. As a consequence, we need to make sure we
// only ever run it once, for the "starting" project.
def snykMergedDepsConfExecuted = false
allprojects { everyProj ->
    task snykResolvedDepsJson {
        def onlyProj = project.hasProperty('onlySubProject') ? onlySubProject : null

        def confNameFilter = (project.hasProperty('configuration')
            ? Pattern.compile(configuration, Pattern.CASE_INSENSITIVE)
            : /.*/
        )
        def confAttrSpec = (project.hasProperty('confAttr')
            ? confAttr.toLowerCase().split(',').collect { it.split(':') }
            : null
        )

        // currentChain is a protection against dependency cycles, which are perfectly normal in Java/Gradle world
        // This function converts a Gradle dependency tree into DepTree structure used by Snyk CLI
        def depsToDict
        depsToDict = { Iterable deps, Set currentChain ->
            def res = [:]
            deps.each { d ->
                def depName = d.moduleGroup + ':' + d.moduleName
                if (!currentChain.contains(depName)) {
                    def row = ['name': depName, 'version': d.moduleVersion, 'conf': d.configuration]
                    currentChain.add(depName)
                    def subDeps = depsToDict(d.children, currentChain)
                    currentChain.remove(depName)
                    if (subDeps.size() > 0) {
                        row['dependencies'] = subDeps
                    }
                    // In Gradle 2, there can be several instances of the same dependency present at each level,
                    // each for a different configuration. In this case, we need to merge the dependencies.
                    if (res.containsKey(depName)) {
                        if (subDeps.size() > 0) {
                            if (!res[depName].containsKey('dependencies')) {
                                res[depName]['dependencies'] = [:]
                            }
                            res[depName]['dependencies'].putAll(subDeps)
                        }
                    } else {
                        res[depName] = row
                    }
                }
            }
            return res
        }

        def matchesAttributeFilter
        matchesAttributeFilter = { conf ->
            if (!conf.hasProperty('attributes')) {
                // Gradle before version 3 does not support attributes
                return true
            }
            def matches = true
            def attrs = conf.attributes
            attrs.keySet().each({ attr ->
                def attrValueAsString = attrs.getAttribute(attr).toString().toLowerCase()
                confAttrSpec.each({ keyValueFilter ->
                    // attr.name is a class name, e.g. com.android.build.api.attributes.BuildTypeAttr
                    if (attr.name.toLowerCase().contains(keyValueFilter[0])
                            && attrValueAsString != keyValueFilter[1]) {
                        matches = false
                    }
                })
            })
            return matches
        }

        def isMatchingConfiguration
        isMatchingConfiguration = {
            it.name != 'snykMergedDepsConf' && it.name =~ confNameFilter && matchesAttributeFilter(conf)
        }

        doLast { task ->
            if (!snykMergedDepsConfExecuted) {
                println('SNYKECHO snykResolvedDepsJson task is executing via doLast')
                def projectsDict = [:]
                def defaultProjectName = task.project.name
                def result = [
                    'defaultProject': defaultProjectName,
                    'projects': projectsDict,
                    'allSubProjectNames': allprojects.collect { it.name }
                ]

                def shouldScanProject = {
                    onlyProj == null ||
                    (onlyProj == '.' && it.name == defaultProjectName) ||
                    it.name == onlyProj
                }

                // First pass: scan all configurations that match the attribute filter and collect all attributes
                // from them, to use unambiguous values of the attributes on the merged configuration.
                //
                // Why we need to scan all sub-projects: if a project A depends on B, and only B has some
                // configurations with attribute C, we still might need attribute C in our configuration
                // when resolving the project A, so that it selects a concrete variant of dependency B.
                def allConfigurationAttributes = [:] // Map<Attribute<?>, Set<?>>
                def attributesAsStrings = [:] // Map<String, Set<string>>
                rootProject.allprojects.each { proj ->
                    proj.configurations.findAll({ it.name != 'snykMergedDepsConf' && it.name =~ confNameFilter && matchesAttributeFilter(it) }).each { conf ->
                        if (!conf.hasProperty('attributes')) {
                           // Gradle before version 3 does not support attributes
                            return
                        }
                        def attrs = conf.attributes
                        attrs.keySet().each({ attr ->
                            def value = attrs.getAttribute(attr)
                            if (!allConfigurationAttributes.containsKey(attr)) {
                                allConfigurationAttributes[attr] = new HashSet()
                                attributesAsStrings[attr.name] = new HashSet()
                            }
                            allConfigurationAttributes[attr].add(value)
                            attributesAsStrings[attr.name].add(value.toString())
                        })
                    }
                }

                // These will be used to suggest attribute filtering to the user if the scan fails
                // due to ambiguous resolution of dependency variants
                println("JSONATTRS " + JsonOutput.toJson(attributesAsStrings))

                rootProject.allprojects.findAll(shouldScanProject).each { proj ->
                    println('SNYKECHO processing project: ' + proj.name)
                    def snykConf = null
                    def mergeableConfs = proj.configurations
                        .findAll({ it.name != 'snykMergedDepsConf' && it.name =~ confNameFilter })

                    // Drop all the configrations that don't match the attribute filter
                    if (confAttrSpec != null) {
                        mergeableConfs = mergeableConfs.findAll(matchesAttributeFilter)
                    }

                    if (mergeableConfs.size() == 0 && proj.configurations.size() > 0) {

                        throw new RuntimeException('Matching configurations not found: ' + confNameFilter +
                                ', available configurations for project ' + proj + ': '
                                + proj.configurations.collect { it.name })

                    } else if (mergeableConfs.size() == 1) {

                        // We use the only matching configuration, with its attributes.
                        snykConf = mergeableConfs.first()

                    } else if (mergeableConfs.size() > 1) {

                        println('SNYKECHO constructing merged configuration from ' + mergeableConfs.collect { conf -> conf.name })
                        // We create a new, "merged" configuration here.
                        snykConf = proj.configurations.create('snykMergedDepsConf')

                        mergeableConfs.each { snykConf.extendsFrom(it) }

                        // Copy all the unambiguous build attributes into the merged configuration
                        // Gradle before version 3 does not support attributes
                        if (snykConf.hasProperty('attributes')) {
                            allConfigurationAttributes.each({ attr, valueSet ->
                                if (valueSet.size() == 1) {
                                    snykConf.attributes.attribute(attr, valueSet.head())
                                }
                            })
                        }
                    }
                    if (snykConf != null) {
                        println('SNYKECHO resolving configuration ' + snykConf.name)
                        def gradleDeps = snykConf.resolvedConfiguration.firstLevelModuleDependencies
                        println('SNYKECHO converting the dependency graph to the DepTree format')
                        projectsDict[proj.name] = [
                            'targetFile': findProject(proj.path).buildFile.toString(),
                            'depDict': depsToDict(gradleDeps, new HashSet())
                        ]
                    } else {
                        projectsDict[proj.name] = [
                            'targetFile': findProject(proj.path).buildFile.toString()
                        ]
                    }
                }
                println("JSONDEPS " + JsonOutput.toJson(result))
                snykMergedDepsConfExecuted = true
            }
        }
    }
}