Skip to content

Commit 9d171b7

Browse files
authored
Added curation audit for gradle (jfrog#455)
1 parent 1351e15 commit 9d171b7

File tree

6 files changed

+197
-5
lines changed

6 files changed

+197
-5
lines changed

commands/audit/sca/java/gradle.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ func getRemoteRepos(depsRepo string, server *config.ServerDetails) (string, stri
122122
if err != nil {
123123
return "", "", err
124124
}
125-
126125
constructedDepsRepo, err := getDepTreeArtifactoryRepository(depsRepo, server)
127126
if err != nil {
128127
return "", "", err
@@ -136,7 +135,6 @@ func constructReleasesRemoteRepo() (string, error) {
136135
if err != nil || serverId == "" || repoName == "" {
137136
return "", err
138137
}
139-
140138
releasesServer, err := config.GetSpecificConfig(serverId, false, true)
141139
if err != nil {
142140
return "", err
@@ -148,12 +146,12 @@ func constructReleasesRemoteRepo() (string, error) {
148146
}
149147

150148
func (gdt *gradleDepTreeManager) execGradleDepTree(depTreeDir string) (outputFileContent []byte, err error) {
149+
151150
gradleExecPath, err := build.GetGradleExecPath(gdt.useWrapper)
152151
if err != nil {
153152
err = errorutils.CheckError(err)
154153
return
155154
}
156-
157155
outputFilePath := filepath.Join(depTreeDir, gradleDepTreeOutputFile)
158156
tasks := []string{
159157
"clean",
@@ -169,7 +167,6 @@ func (gdt *gradleDepTreeManager) execGradleDepTree(depTreeDir string) (outputFil
169167
defer func() {
170168
err = errors.Join(err, errorutils.CheckError(os.Remove(outputFilePath)))
171169
}()
172-
173170
outputFileContent, err = os.ReadFile(outputFilePath)
174171
err = errorutils.CheckError(err)
175172
return

commands/curation/curationaudit.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const (
7575
MinArtiGolangSupport = "7.87.0"
7676
MinArtiNuGetSupport = "7.93.0"
7777
MinXrayPassThroughSupport = "3.92.0"
78+
MinArtiGradlesupport = "7.63.5"
7879
)
7980

8081
var CurationOutputFormats = []string{string(outFormat.Table), string(outFormat.Json)}
@@ -93,6 +94,9 @@ var supportedTech = map[techutils.Technology]func(ca *CurationAuditCommand) (boo
9394
techutils.Nuget: func(ca *CurationAuditCommand) (bool, error) {
9495
return ca.checkSupportByVersionOrEnv(techutils.Nuget, MinArtiNuGetSupport)
9596
},
97+
techutils.Gradle: func(ca *CurationAuditCommand) (bool, error) {
98+
return ca.checkSupportByVersionOrEnv(techutils.Gradle, MinArtiGradlesupport)
99+
},
96100
}
97101

98102
func (ca *CurationAuditCommand) checkSupportByVersionOrEnv(tech techutils.Technology, minArtiVersion string) (bool, error) {
@@ -400,6 +404,8 @@ func (ca *CurationAuditCommand) getAuditParamsByTech(tech techutils.Technology)
400404
SetNpmOverwritePackageLock(true)
401405
case techutils.Maven:
402406
ca.AuditParams.SetIsMavenDepTreeInstalled(true)
407+
case techutils.Gradle:
408+
ca.AuditParams.SetIsGradleDepTreeInstalled(false)
403409
}
404410

405411
return ca.AuditParams
@@ -908,7 +914,8 @@ func getUrlNameAndVersionByTech(tech techutils.Technology, node *xrayUtils.Graph
908914
return getNpmNameScopeAndVersion(node.Id, artiUrl, repo, techutils.Npm.String())
909915
case techutils.Maven:
910916
return getMavenNameScopeAndVersion(node.Id, artiUrl, repo, node)
911-
917+
case techutils.Gradle:
918+
return getGradleNameScopeAndVersion(node.Id, artiUrl, repo, node)
912919
case techutils.Pip:
913920
downloadUrls, name, version = getPythonNameVersion(node.Id, downloadUrlsMap)
914921
return
@@ -1003,6 +1010,43 @@ func getMavenNameScopeAndVersion(id, artiUrl, repo string, node *xrayUtils.Graph
10031010
return downloadUrls, strings.Join(allParts[:2], ":"), "", allParts[2]
10041011
}
10051012

1013+
// Given an input containing a classifier, e.g., id: gav://org.apache.tomcat.embed:tomcat-embed-jasper:8.0.33-jdk15,
1014+
// we parse it to extract the package name and version, then use that information to build the corresponding Artifactory download URL.
1015+
func getGradleNameScopeAndVersion(id, artiUrl, repo string, node *xrayUtils.GraphNode) (downloadUrls []string, name, scope, version string) {
1016+
id = strings.TrimPrefix(id, "gav://")
1017+
parts := strings.Split(id, ":")
1018+
if len(parts) < 3 {
1019+
return
1020+
}
1021+
1022+
groupID, artifactID, version := parts[0], parts[1], parts[2]
1023+
nameVersion := artifactID + "-" + version
1024+
versionDir := version
1025+
1026+
if node != nil && node.Classifier != nil && *node.Classifier != "" {
1027+
classifierSuffix := "-" + *node.Classifier
1028+
versionDir = strings.TrimSuffix(version, classifierSuffix)
1029+
}
1030+
1031+
groupPath := strings.ReplaceAll(groupID, ".", "/")
1032+
packagePath := fmt.Sprintf("%s/%s/%s/%s", groupPath, artifactID, versionDir, nameVersion)
1033+
if node != nil && node.Types != nil {
1034+
for _, fileType := range *node.Types {
1035+
if fileType == "jar" {
1036+
jarURL := fmt.Sprintf("%s/%s/%s.jar", strings.TrimSuffix(artiUrl, "/"), repo, packagePath)
1037+
downloadUrls = append(downloadUrls, jarURL)
1038+
break
1039+
}
1040+
}
1041+
} else {
1042+
// .jar type file by default
1043+
jarURL := fmt.Sprintf("%s/%s/%s.jar", strings.TrimSuffix(artiUrl, "/"), repo, packagePath)
1044+
downloadUrls = append(downloadUrls, jarURL)
1045+
}
1046+
1047+
return downloadUrls, strings.Join(parts[:2], ":"), "", parts[2]
1048+
}
1049+
10061050
// The graph holds, for each node, the component ID (xray representation)
10071051
// from which we extract the package name, version, and construct the Artifactory download URL.
10081052
func getNpmNameScopeAndVersion(id, artiUrl, repo, tech string) (downloadUrl []string, name, scope, version string) {

commands/curation/curationaudit_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,63 @@ func getTestCasesForDoCurationAudit() []testCase {
631631
},
632632
},
633633
},
634+
{
635+
name: "gradle tree - one blocked package",
636+
tech: techutils.Gradle,
637+
pathToProject: filepath.Join("projects", "package-managers", "gradle", "curation-project"),
638+
funcToGetGoals: func(t *testing.T) []string {
639+
// To ensure only the blocked package is resolved during testing, we pre-populate the cache with dependencies beforehand.
640+
// Since the cache location depends on the project directory, we need to mimic that setup during the pretest build.
641+
// This way, the test phase will use the same cache directory, already filled with required dependencies.
642+
restoreWD := testUtils.ChangeWDWithCallback(t, "tests/testdata/projects/package-managers")
643+
defer restoreWD()
644+
645+
curationCache, err := utils.GetCurationCacheFolderByTech(techutils.Gradle)
646+
require.NoError(t, err)
647+
648+
return []string{
649+
"gradle", "build",
650+
"--build-file", "build.gradle",
651+
"--gradle-user-home=" + curationCache,
652+
"--no-daemon",
653+
}
654+
},
655+
serveResources: map[string]string{
656+
"build.gradle": filepath.Join("tests", "testdata", "projects", "package-managers", "gradle", "curation-project", "build.gradle"),
657+
},
658+
requestToFail: map[string]bool{
659+
"/gradle-virtual/log4j/log4j/1.2.14/log4j-1.2.14.jar": true,
660+
},
661+
expectedResp: map[string]*CurationReport{
662+
"com.example:curation-project:1.0.0": {
663+
// Ensure packagesStatus is properly initialized, even if empty initially
664+
packagesStatus: []*PackageStatus{
665+
{
666+
Action: "blocked",
667+
ParentName: "log4j:log4j",
668+
ParentVersion: "1.2.14",
669+
BlockedPackageUrl: "/gradle-virtual/log4j/log4j/1.2.14/log4j-1.2.14.jar",
670+
PackageName: "log4j:log4j",
671+
PackageVersion: "1.2.14",
672+
BlockingReason: "Policy violations",
673+
DepRelation: "direct",
674+
PkgType: "gradle",
675+
WaiverAllowed: false,
676+
Policy: []Policy{
677+
{
678+
Policy: "pol1",
679+
Condition: "cond1",
680+
Explanation: "",
681+
Recommendation: "",
682+
},
683+
},
684+
},
685+
},
686+
totalNumberOfPackages: 5, // Adjust the number if necessary
687+
},
688+
},
689+
allowInsecureTls: true,
690+
},
634691
{
635692
name: "python tree - one blocked package",
636693
tech: techutils.Pip,
@@ -946,6 +1003,41 @@ func Test_getGoNameScopeAndVersion(t *testing.T) {
9461003
}
9471004
}
9481005

1006+
func Test_getGradleNameScopeAndVersion(t *testing.T) {
1007+
tests := []struct {
1008+
name string
1009+
id string
1010+
artiUrl string
1011+
repo string
1012+
node string
1013+
wantDownloadUrls []string
1014+
wantName string
1015+
wantScope string
1016+
wantVersion string
1017+
}{
1018+
{
1019+
name: "Realistic package from example - log4j",
1020+
id: "gav://log4j:log4j:1.2.14",
1021+
artiUrl: "http://test.jfrog.io/artifactory",
1022+
repo: "gradle-virtual",
1023+
node: "",
1024+
wantDownloadUrls: []string{"http://test.jfrog.io/artifactory/gradle-virtual/log4j/log4j/1.2.14/log4j-1.2.14.jar"},
1025+
wantName: "log4j:log4j",
1026+
wantScope: "",
1027+
wantVersion: "1.2.14",
1028+
},
1029+
}
1030+
for _, tt := range tests {
1031+
t.Run(tt.name, func(t *testing.T) {
1032+
gotDownloadUrls, gotName, gotScope, gotVersion := getGradleNameScopeAndVersion(tt.id, tt.artiUrl, tt.repo, nil)
1033+
assert.Equal(t, tt.wantDownloadUrls, gotDownloadUrls, "downloadUrls mismatch")
1034+
assert.Equal(t, tt.wantName, gotName, "name mismatch")
1035+
assert.Equal(t, tt.wantScope, gotScope, "scope mismatch")
1036+
assert.Equal(t, tt.wantVersion, gotVersion, "version mismatch")
1037+
})
1038+
}
1039+
}
1040+
9491041
func Test_getNugetNameScopeAndVersion(t *testing.T) {
9501042
tests := []struct {
9511043
name string
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: 1
2+
type: gradle
3+
resolver:
4+
repo: gradle-virtual
5+
serverId: test
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Apply the Java plugin to add support for Java
2+
plugins {
3+
id 'java'
4+
}
5+
6+
// Set the group and version of the project
7+
group = 'com.example'
8+
version = '1.0.0'
9+
10+
// Specify the repositories for dependencies
11+
repositories {
12+
// Use Maven Central repository
13+
//mavenCentral()
14+
maven {
15+
allowInsecureProtocol = true
16+
url "http://localhost:8046/artifactory/gradle-virtual/"
17+
credentials {
18+
username = 'admin'
19+
password = 'password'
20+
}
21+
}
22+
}
23+
24+
// Declare the dependencies for the project
25+
dependencies {
26+
// Use JUnit 4 for testing
27+
testImplementation 'junit:junit:4.13.2'
28+
implementation 'org.apache.commons:commons-lang3:3.12.0'
29+
implementation group: 'ch.qos.logback', name: 'logback-access', version: '1.4.13'
30+
implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.5.4'
31+
implementation 'log4j:log4j:1.2.14'
32+
}
33+
34+
// Define a custom task (optional)
35+
task hello {
36+
doLast {
37+
println 'Hello, Gradle!'
38+
}
39+
}
40+
41+
// Specify the Java version compatibility
42+
java {
43+
sourceCompatibility = JavaVersion.VERSION_11
44+
targetCompatibility = JavaVersion.VERSION_11
45+
}

utils/auditbasicparams.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type AuditParams interface {
3737
SetIgnoreConfigFile(ignoreConfigFile bool) *AuditBasicParams
3838
IsMavenDepTreeInstalled() bool
3939
SetIsMavenDepTreeInstalled(isMavenDepTreeInstalled bool) *AuditBasicParams
40+
SetIsGradleDepTreeInstalled(isGradleDepTreeInstalled bool) *AuditBasicParams
4041
IsCurationCmd() bool
4142
SetIsCurationCmd(bool) *AuditBasicParams
4243
SetExclusions(exclusions []string) *AuditBasicParams
@@ -59,6 +60,7 @@ type AuditBasicParams struct {
5960
insecureTls bool
6061
ignoreConfigFile bool
6162
isMavenDepTreeInstalled bool
63+
isGradleDepTreeInstalled bool
6264
isCurationCmd bool
6365
maxTreeDepth string
6466
pipRequirementsFile string
@@ -258,6 +260,13 @@ func (abp *AuditBasicParams) SetIsMavenDepTreeInstalled(isMavenDepTreeInstalled
258260
abp.isMavenDepTreeInstalled = isMavenDepTreeInstalled
259261
return abp
260262
}
263+
func (abp *AuditBasicParams) IsGradleDepTreeInstalled() bool {
264+
return abp.isGradleDepTreeInstalled
265+
}
266+
func (abp *AuditBasicParams) SetIsGradleDepTreeInstalled(isGradleDepTreeInstalled bool) *AuditBasicParams {
267+
abp.isGradleDepTreeInstalled = isGradleDepTreeInstalled
268+
return abp
269+
}
261270

262271
func (abp *AuditBasicParams) IsCurationCmd() bool {
263272
return abp.isCurationCmd

0 commit comments

Comments
 (0)