Skip to content

Commit c9a9de4

Browse files
committed
Added-unit-test
1 parent 420e2e7 commit c9a9de4

File tree

2 files changed

+288
-2
lines changed

2 files changed

+288
-2
lines changed

commands/curation/curationaudit_test.go

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ func TestDoCurationAudit(t *testing.T) {
427427
cleanUp := createCurationTestEnv(t, basePathToTests, tt, config)
428428
defer cleanUp()
429429
// Create audit command, and run it
430-
results, err := createCurationCmdAndRun(tt)
430+
results, err := createCurationCmdAndRun(tt, config)
431431
// Validate the results
432432
if tt.requestToError == nil {
433433
assert.NoError(t, err)
@@ -505,14 +505,19 @@ func runPreTestExec(t *testing.T, basePathToTests string, testCase testCase) {
505505
callbackPreTest()
506506
}
507507

508-
func createCurationCmdAndRun(tt testCase) (cmdResults map[string]*CurationReport, err error) {
508+
func createCurationCmdAndRun(tt testCase, serverDetails *config.ServerDetails) (cmdResults map[string]*CurationReport, err error) {
509509
curationCmd := NewCurationAuditCommand()
510510
curationCmd.SetIsCurationCmd(true)
511511
curationCmd.parallelRequests = 3
512512
// For tests, we use localhost http server (nuget have issues without setting insecureTls)
513513
curationCmd.SetInsecureTls(true)
514514
curationCmd.SetIgnoreConfigFile(tt.shouldIgnoreConfigFile)
515515
curationCmd.SetInsecureTls(tt.allowInsecureTls)
516+
if tt.dockerImageName != "" {
517+
curationCmd.SetDockerImageName(tt.dockerImageName)
518+
// Docker requires server details to be set explicitly
519+
curationCmd.SetServerDetails(serverDetails)
520+
}
516521
cmdResults = map[string]*CurationReport{}
517522
err = curationCmd.doCurateAudit(cmdResults)
518523
return
@@ -568,6 +573,7 @@ type testCase struct {
568573
tech techutils.Technology
569574
createServerWithoutCreds bool
570575
allowInsecureTls bool
576+
dockerImageName string
571577
}
572578

573579
func (tc testCase) getPathToTests() string {
@@ -983,6 +989,43 @@ func getTestCasesForDoCurationAudit() []testCase {
983989
},
984990
allowInsecureTls: true,
985991
},
992+
{
993+
name: "docker tree - one blocked package",
994+
tech: techutils.Docker,
995+
pathToProject: filepath.Join("projects", "package-managers", "docker", "curation-project"),
996+
dockerImageName: "repo-test-docker/dweomer/nginx-auth-ldap:1.13.5-on-alpine-3.5",
997+
requestToFail: map[string]bool{
998+
"/api/docker/repo-test-docker/v2/dweomer/nginx-auth-ldap/manifests/1.13.5-on-alpine-3.5": true,
999+
},
1000+
expectedRequest: map[string]bool{
1001+
"/api/docker/repo-test-docker/v2/dweomer/nginx-auth-ldap/manifests/1.13.5-on-alpine-3.5": false,
1002+
},
1003+
expectedResp: map[string]*CurationReport{
1004+
"root:latest": {
1005+
packagesStatus: []*PackageStatus{
1006+
{
1007+
Action: "blocked",
1008+
ParentName: "dweomer/nginx-auth-ldap",
1009+
ParentVersion: "1.13.5-on-alpine-3.5",
1010+
BlockedPackageUrl: "/api/docker/repo-test-docker/v2/dweomer/nginx-auth-ldap/manifests/1.13.5-on-alpine-3.5",
1011+
PackageName: "dweomer/nginx-auth-ldap",
1012+
PackageVersion: "1.13.5-on-alpine-3.5",
1013+
DepRelation: "direct",
1014+
PkgType: "docker",
1015+
BlockingReason: "Policy violations",
1016+
Policy: []Policy{
1017+
{
1018+
Policy: "pol1",
1019+
Condition: "cond1",
1020+
},
1021+
},
1022+
},
1023+
},
1024+
totalNumberOfPackages: 0,
1025+
},
1026+
},
1027+
allowInsecureTls: true,
1028+
},
9861029
}
9871030
return tests
9881031
}
@@ -1185,6 +1228,99 @@ func Test_getGemNameScopeAndVersion(t *testing.T) {
11851228
}
11861229
}
11871230

1231+
func Test_getDockerNameScopeAndVersion(t *testing.T) {
1232+
tests := []struct {
1233+
name string
1234+
id string
1235+
artiUrl string
1236+
repo string
1237+
wantDownloadUrls []string
1238+
wantName string
1239+
wantScope string
1240+
wantVersion string
1241+
}{
1242+
{
1243+
name: "Basic docker image with tag",
1244+
id: "docker://nginx:1.21.0",
1245+
artiUrl: "http://test.jfrog.io/artifactory",
1246+
repo: "docker-remote",
1247+
wantDownloadUrls: []string{"http://test.jfrog.io/artifactory/api/docker/docker-remote/v2/nginx/manifests/1.21.0"},
1248+
wantName: "nginx",
1249+
wantScope: "",
1250+
wantVersion: "1.21.0",
1251+
},
1252+
{
1253+
name: "Docker image with registry prefix",
1254+
id: "docker://registry.example.com/nginx:1.21.0",
1255+
artiUrl: "http://test.jfrog.io/artifactory",
1256+
repo: "docker-remote",
1257+
wantDownloadUrls: []string{"http://test.jfrog.io/artifactory/api/docker/docker-remote/v2/registry.example.com/nginx/manifests/1.21.0"},
1258+
wantName: "registry.example.com/nginx",
1259+
wantScope: "",
1260+
wantVersion: "1.21.0",
1261+
},
1262+
{
1263+
name: "Docker image with sha256 digest",
1264+
id: "docker://nginx:sha256:abc123def456",
1265+
artiUrl: "http://test.jfrog.io/artifactory",
1266+
repo: "docker-remote",
1267+
wantDownloadUrls: []string{"http://test.jfrog.io/artifactory/api/docker/docker-remote/v2/nginx/manifests/sha256:abc123def456"},
1268+
wantName: "nginx",
1269+
wantScope: "",
1270+
wantVersion: "sha256:abc123def456",
1271+
},
1272+
{
1273+
name: "Docker image without version defaults to latest",
1274+
id: "docker://nginx",
1275+
artiUrl: "http://test.jfrog.io/artifactory",
1276+
repo: "docker-remote",
1277+
wantDownloadUrls: []string{"http://test.jfrog.io/artifactory/api/docker/docker-remote/v2/nginx/manifests/latest"},
1278+
wantName: "nginx",
1279+
wantScope: "",
1280+
wantVersion: "latest",
1281+
},
1282+
{
1283+
name: "Empty id returns empty values",
1284+
id: "",
1285+
artiUrl: "http://test.jfrog.io/artifactory",
1286+
repo: "docker-remote",
1287+
wantDownloadUrls: nil,
1288+
wantName: "",
1289+
wantScope: "",
1290+
wantVersion: "",
1291+
},
1292+
{
1293+
name: "Without artiUrl and repo, no download URL",
1294+
id: "docker://nginx:1.21.0",
1295+
artiUrl: "",
1296+
repo: "",
1297+
wantDownloadUrls: nil,
1298+
wantName: "nginx",
1299+
wantScope: "",
1300+
wantVersion: "1.21.0",
1301+
},
1302+
{
1303+
name: "Artifactory URL with trailing slash",
1304+
id: "docker://nginx:1.21.0",
1305+
artiUrl: "http://test.jfrog.io/artifactory/",
1306+
repo: "docker-remote",
1307+
wantDownloadUrls: []string{"http://test.jfrog.io/artifactory/api/docker/docker-remote/v2/nginx/manifests/1.21.0"},
1308+
wantName: "nginx",
1309+
wantScope: "",
1310+
wantVersion: "1.21.0",
1311+
},
1312+
}
1313+
for _, tt := range tests {
1314+
t.Run(tt.name, func(t *testing.T) {
1315+
gotDownloadUrls, gotName, gotScope, gotVersion := getDockerNameScopeAndVersion(tt.id, tt.artiUrl, tt.repo)
1316+
assert.Equal(t, tt.wantDownloadUrls, gotDownloadUrls, "downloadUrls mismatch")
1317+
assert.Equal(t, tt.wantName, gotName, "name mismatch")
1318+
assert.Equal(t, tt.wantScope, gotScope, "scope mismatch")
1319+
assert.Equal(t, tt.wantVersion, gotVersion, "version mismatch")
1320+
})
1321+
}
1322+
}
1323+
11881324
func Test_getNugetNameScopeAndVersion(t *testing.T) {
11891325
tests := []struct {
11901326
name string
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package docker
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
"testing"
7+
8+
coreCommonTests "github.com/jfrog/jfrog-cli-core/v2/common/tests"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies"
13+
)
14+
15+
func TestBuildDependencyTree(t *testing.T) {
16+
tests := []struct {
17+
name string
18+
dockerImageName string
19+
expectedUniqueDeps []string
20+
expectError bool
21+
}{
22+
{
23+
name: "Valid docker image with repo and tag",
24+
dockerImageName: "my-repo/my-image:v1.0.0",
25+
expectedUniqueDeps: []string{"docker://my-image:v1.0.0"},
26+
expectError: false,
27+
},
28+
{
29+
name: "Docker image with library prefix",
30+
dockerImageName: "my-repo/library/my-image:latest",
31+
expectedUniqueDeps: []string{"docker://my-image:latest"},
32+
expectError: false,
33+
},
34+
{
35+
name: "Docker image without tag (defaults to latest)",
36+
dockerImageName: "my-repo/my-image",
37+
expectedUniqueDeps: []string{"docker://my-image:latest"},
38+
expectError: false,
39+
},
40+
{
41+
name: "Empty docker image name",
42+
dockerImageName: "",
43+
expectedUniqueDeps: nil,
44+
expectError: true,
45+
},
46+
{
47+
name: "Invalid format - no repo",
48+
dockerImageName: "image:tag",
49+
expectedUniqueDeps: nil,
50+
expectError: true,
51+
},
52+
}
53+
54+
for _, tt := range tests {
55+
t.Run(tt.name, func(t *testing.T) {
56+
params := technologies.BuildInfoBomGeneratorParams{
57+
DockerImageName: tt.dockerImageName,
58+
}
59+
_, uniqueDeps, err := BuildDependencyTree(params)
60+
61+
if tt.expectError {
62+
assert.Error(t, err)
63+
} else {
64+
assert.NoError(t, err)
65+
assert.ElementsMatch(t, uniqueDeps, tt.expectedUniqueDeps, "Unique dependencies mismatch. First is actual, Second is Expected")
66+
}
67+
})
68+
}
69+
}
70+
func TestBuildDependencyTree_MultiArch(t *testing.T) {
71+
manifestListResponse := `{
72+
"schemaVersion": 2,
73+
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
74+
"manifests": [
75+
{
76+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
77+
"size": 2413,
78+
"digest": "sha256:3446f171923148a8e1ef9ed402f6eefcf69811c2b25cd969e13ef175a310836d",
79+
"platform": {
80+
"architecture": "amd64",
81+
"os": "linux"
82+
}
83+
},
84+
{
85+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
86+
"size": 2413,
87+
"digest": "sha256:cb7bf93be94a38ca93a8dbca4468ce86079c6c83aacc8d603090db29fcaaf7b8",
88+
"platform": {
89+
"architecture": "arm64",
90+
"os": "linux"
91+
}
92+
},
93+
{
94+
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
95+
"size": 2412,
96+
"digest": "sha256:27ac676b8471b951f257b1349c6f69b5f8738499494f89270f48b3c798beada4",
97+
"platform": {
98+
"architecture": "arm",
99+
"os": "linux",
100+
"variant": "v7"
101+
}
102+
}
103+
]
104+
}`
105+
106+
serverMock, serverDetails, _ := coreCommonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
107+
if r.Method == http.MethodHead {
108+
if strings.Contains(r.RequestURI, "/api/docker/") && strings.Contains(r.RequestURI, "/manifests/") {
109+
w.Header().Set("Content-Type", "application/vnd.docker.distribution.manifest.list.v2+json")
110+
w.WriteHeader(http.StatusOK)
111+
}
112+
}
113+
if r.Method == http.MethodGet {
114+
if strings.Contains(r.RequestURI, "/api/docker/") && strings.Contains(r.RequestURI, "/manifests/") {
115+
w.Header().Set("Content-Type", "application/json")
116+
w.WriteHeader(http.StatusOK)
117+
w.Write([]byte(manifestListResponse))
118+
}
119+
}
120+
})
121+
defer serverMock.Close()
122+
123+
serverDetails.ArtifactoryUrl = serverDetails.Url + "artifactory"
124+
125+
params := technologies.BuildInfoBomGeneratorParams{
126+
DockerImageName: "my-repo/my-image:v1.0.0",
127+
ServerDetails: serverDetails,
128+
}
129+
130+
trees, uniqueDeps, err := BuildDependencyTree(params)
131+
assert.NoError(t, err)
132+
assert.NotNil(t, trees)
133+
134+
expectedUniqueDeps := []string{
135+
"docker://my-image:sha256:3446f171923148a8e1ef9ed402f6eefcf69811c2b25cd969e13ef175a310836d",
136+
"docker://my-image:sha256:cb7bf93be94a38ca93a8dbca4468ce86079c6c83aacc8d603090db29fcaaf7b8",
137+
"docker://my-image:sha256:27ac676b8471b951f257b1349c6f69b5f8738499494f89270f48b3c798beada4",
138+
}
139+
assert.ElementsMatch(t, uniqueDeps, expectedUniqueDeps, "Unique dependencies mismatch. First is actual, Second is Expected")
140+
141+
require.Len(t, trees, 1)
142+
assert.Equal(t, "root", trees[0].Id)
143+
assert.Len(t, trees[0].Nodes, 3)
144+
145+
nodeIds := make([]string, 0, len(trees[0].Nodes))
146+
for _, node := range trees[0].Nodes {
147+
nodeIds = append(nodeIds, node.Id)
148+
}
149+
assert.ElementsMatch(t, nodeIds, expectedUniqueDeps)
150+
}

0 commit comments

Comments
 (0)