mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
feat: gradle lockfile support (#1719)
Signed-off-by: Henry Sachs <Henry.Sachs@deutschebahn.com>
This commit is contained in:
parent
da44db92e9
commit
0fed17f1c8
@ -73,6 +73,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
|
||||
java.NewJavaCataloger(cfg.Java()),
|
||||
java.NewJavaPomCataloger(),
|
||||
java.NewNativeImageCataloger(),
|
||||
java.NewJavaGradleLockfileCataloger(),
|
||||
apkdb.NewApkdbCataloger(),
|
||||
golang.NewGoModuleBinaryCataloger(cfg.Go()),
|
||||
golang.NewGoModFileCataloger(cfg.Go()),
|
||||
@ -107,6 +108,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
|
||||
java.NewJavaCataloger(cfg.Java()),
|
||||
java.NewJavaPomCataloger(),
|
||||
java.NewNativeImageCataloger(),
|
||||
java.NewJavaGradleLockfileCataloger(),
|
||||
apkdb.NewApkdbCataloger(),
|
||||
golang.NewGoModuleBinaryCataloger(cfg.Go()),
|
||||
golang.NewGoModFileCataloger(cfg.Go()),
|
||||
|
||||
@ -159,10 +159,39 @@ func TestParseJar(t *testing.T) {
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: map[string]string{
|
||||
"Manifest-Version": "1.0",
|
||||
"Main-Class": "hello.HelloWorld",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"joda-time": {
|
||||
Name: "joda-time",
|
||||
Version: "2.2",
|
||||
PURL: "pkg:maven/joda-time/joda-time@2.2",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
// ensure that nested packages with different names than that of the parent are appended as
|
||||
// a suffix on the virtual path
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Path: "META-INF/maven/joda-time/joda-time/pom.properties",
|
||||
GroupID: "joda-time",
|
||||
ArtifactID: "joda-time",
|
||||
Version: "2.2",
|
||||
},
|
||||
PomProject: &pkg.PomProject{
|
||||
Path: "META-INF/maven/joda-time/joda-time/pom.xml",
|
||||
GroupID: "joda-time",
|
||||
ArtifactID: "joda-time",
|
||||
Version: "2.2",
|
||||
Name: "Joda time",
|
||||
Description: "Date and time library to replace JDK date handling",
|
||||
URL: "http://joda-time.sourceforge.net",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -31,3 +31,11 @@ func NewJavaPomCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("java-pom-cataloger").
|
||||
WithParserByGlobs(parserPomXML, "**/pom.xml")
|
||||
}
|
||||
|
||||
// NewJavaGradleLockfileCataloger returns a cataloger capable of parsing
|
||||
// dependencies from a gradle.lockfile file.
|
||||
// older versions of lockfiles aren't supported yet
|
||||
func NewJavaGradleLockfileCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger("java-gradle-lockfile-cataloger").
|
||||
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
|
||||
}
|
||||
|
||||
63
syft/pkg/cataloger/java/parse_gradle_lockfile.go
Normal file
63
syft/pkg/cataloger/java/parse_gradle_lockfile.go
Normal file
@ -0,0 +1,63 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const gradleLockfileGlob = "**/gradle.lockfile*"
|
||||
|
||||
// Dependency represents a single dependency in the gradle.lockfile file
|
||||
type LockfileDependency struct {
|
||||
Group string
|
||||
Name string
|
||||
Version string
|
||||
}
|
||||
|
||||
func parseGradleLockfile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var pkgs []pkg.Package
|
||||
|
||||
// Create a new scanner to read the file
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// Create slices to hold the dependencies and plugins
|
||||
dependencies := []LockfileDependency{}
|
||||
|
||||
// Loop over all lines in the file
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Trim leading and trailing whitespace from the line
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
groupNameVersion := line
|
||||
groupNameVersion = strings.Split(groupNameVersion, "=")[0]
|
||||
parts := strings.Split(groupNameVersion, ":")
|
||||
|
||||
// we have a version directly specified
|
||||
if len(parts) == 3 {
|
||||
// Create a new Dependency struct and add it to the dependencies slice
|
||||
dep := LockfileDependency{Group: parts[0], Name: parts[1], Version: parts[2]}
|
||||
dependencies = append(dependencies, dep)
|
||||
}
|
||||
}
|
||||
// map the dependencies
|
||||
for _, dep := range dependencies {
|
||||
mappedPkg := pkg.Package{
|
||||
Name: dep.Name,
|
||||
Version: dep.Version,
|
||||
Locations: source.NewLocationSet(reader.Location),
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
}
|
||||
pkgs = append(pkgs, mappedPkg)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
52
syft/pkg/cataloger/java/parse_gradle_lockfile_test.go
Normal file
52
syft/pkg/cataloger/java/parse_gradle_lockfile_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_parserGradleLockfile(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
input: "test-fixtures/gradle/gradle.lockfile",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "hamcrest-core",
|
||||
Version: "1.3",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
},
|
||||
{
|
||||
Name: "joda-time",
|
||||
Version: "2.2",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
},
|
||||
{
|
||||
Name: "junit",
|
||||
Version: "4.12",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
for i := range test.expected {
|
||||
test.expected[i].Locations.Add(source.NewLocation(test.input))
|
||||
}
|
||||
pkgtest.TestFileParser(t, test.input, parseGradleLockfile, test.expected, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
1
syft/pkg/cataloger/java/test-fixtures/gradle/.gitignore
vendored
Normal file
1
syft/pkg/cataloger/java/test-fixtures/gradle/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.gradle
|
||||
58
syft/pkg/cataloger/java/test-fixtures/gradle/build.gradle
Normal file
58
syft/pkg/cataloger/java/test-fixtures/gradle/build.gradle
Normal file
@ -0,0 +1,58 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'eclipse'
|
||||
id 'application'
|
||||
}
|
||||
|
||||
mainClassName = 'hello.HelloWorld'
|
||||
|
||||
dependencyLocking {
|
||||
lockAllConfigurations()
|
||||
}
|
||||
// tag::repositories[]
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
// end::repositories[]
|
||||
|
||||
// tag::dependencies[]
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
dependencies {
|
||||
implementation "joda-time:joda-time:2.2"
|
||||
testImplementation "junit:junit:4.12"
|
||||
}
|
||||
// end::dependencies[]
|
||||
|
||||
// tag::jar[]
|
||||
jar {
|
||||
archivesBaseName = 'example-java-app-gradle'
|
||||
version = '0.1.0'
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'hello.HelloWorld'
|
||||
)
|
||||
}
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
}
|
||||
// end::jar[]
|
||||
|
||||
// tag::wrapper[]
|
||||
// end::wrapper[]
|
||||
|
||||
// to invoke: gradle resolveAndLockAll --write-locks
|
||||
tasks.register('resolveAndLockAll') {
|
||||
notCompatibleWithConfigurationCache("Filters configurations at execution time")
|
||||
doFirst {
|
||||
assert gradle.startParameter.writeDependencyLocks
|
||||
}
|
||||
doLast {
|
||||
configurations.findAll {
|
||||
// Add any custom filtering on the configurations to be resolved
|
||||
it.canBeResolved
|
||||
}.each { it.resolve() }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
joda-time:joda-time:2.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
junit:junit:4.12=testCompileClasspath,testRuntimeClasspath
|
||||
org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath
|
||||
empty=annotationProcessor,testAnnotationProcessor
|
||||
@ -4,7 +4,7 @@ set -uxe
|
||||
# note: this can be easily done in a 1-liner, however circle CI does NOT allow volume mounts from the host in docker executors (since they are on remote hosts, where the host files are inaccessible)
|
||||
|
||||
PKGSDIR=$1
|
||||
CTRID=$(docker create -u "$(id -u):$(id -g)" -v /example-java-app -w /example-java-app gradle:6.8.3-jdk gradle build)
|
||||
CTRID=$(docker create -u "$(id -u):$(id -g)" -v /example-java-app -w /example-java-app gradle:8.0.2-jdk gradle build)
|
||||
|
||||
function cleanup() {
|
||||
docker rm "${CTRID}"
|
||||
|
||||
@ -1,31 +1,58 @@
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'application'
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'eclipse'
|
||||
id 'application'
|
||||
}
|
||||
|
||||
mainClassName = 'hello.HelloWorld'
|
||||
|
||||
dependencyLocking {
|
||||
lockAllConfigurations()
|
||||
}
|
||||
// tag::repositories[]
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
// end::repositories[]
|
||||
|
||||
// tag::jar[]
|
||||
jar {
|
||||
baseName = 'example-java-app-gradle'
|
||||
version = '0.1.0'
|
||||
}
|
||||
// end::jar[]
|
||||
|
||||
// tag::dependencies[]
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
dependencies {
|
||||
compile "joda-time:joda-time:2.2"
|
||||
testCompile "junit:junit:4.12"
|
||||
implementation "joda-time:joda-time:2.2"
|
||||
testImplementation "junit:junit:4.12"
|
||||
}
|
||||
// end::dependencies[]
|
||||
|
||||
// tag::jar[]
|
||||
jar {
|
||||
archivesBaseName = 'example-java-app-gradle'
|
||||
version = '0.1.0'
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'hello.HelloWorld'
|
||||
)
|
||||
}
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
}
|
||||
// end::jar[]
|
||||
|
||||
// tag::wrapper[]
|
||||
// end::wrapper[]
|
||||
|
||||
// to invoke: gradle resolveAndLockAll --write-locks
|
||||
tasks.register('resolveAndLockAll') {
|
||||
notCompatibleWithConfigurationCache("Filters configurations at execution time")
|
||||
doFirst {
|
||||
assert gradle.startParameter.writeDependencyLocks
|
||||
}
|
||||
doLast {
|
||||
configurations.findAll {
|
||||
// Add any custom filtering on the configurations to be resolved
|
||||
it.canBeResolved
|
||||
}.each { it.resolve() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
joda-time:joda-time:2.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
junit:junit:4.12=testCompileClasspath,testRuntimeClasspath
|
||||
org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath
|
||||
empty=annotationProcessor,testAnnotationProcessor
|
||||
Loading…
x
Reference in New Issue
Block a user