Skip to content

Commit 1105c95

Browse files
fix it:tests and codacy review warning fix
1 parent a7fc362 commit 1105c95

File tree

4 files changed

+156
-60
lines changed

4 files changed

+156
-60
lines changed

cmd/upload_sbom.go

Lines changed: 81 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,46 @@ func executeUploadSBOM(imageRef string) int {
9191
"org": sbomOrg,
9292
})
9393

94-
// Generate SBOM with Trivy
94+
sbomPath, err := generateSBOM(imageRef)
95+
if err != nil {
96+
return 2
97+
}
98+
defer os.Remove(sbomPath)
99+
100+
fmt.Printf("Uploading SBOM to Codacy (org: %s/%s)...\n", sbomProvider, sbomOrg)
101+
params := sbomUploadParams{
102+
provider: sbomProvider,
103+
org: sbomOrg,
104+
apiToken: sbomAPIToken,
105+
repoName: sbomRepoName,
106+
env: sbomEnv,
107+
}
108+
if err := uploadSBOMToCodacy(sbomPath, sbomImageName, tag, params); err != nil {
109+
logger.Error("Failed to upload SBOM", logrus.Fields{"error": err.Error()})
110+
color.Red("Error: Failed to upload SBOM: %v", err)
111+
return 1
112+
}
113+
114+
color.Green("Successfully uploaded SBOM for %s:%s", sbomImageName, tag)
115+
return 0
116+
}
117+
118+
// generateSBOM runs Trivy to generate an SBOM file and returns the path to it.
119+
func generateSBOM(imageRef string) (string, error) {
95120
trivyPath, err := getTrivyPath()
96121
if err != nil {
97122
handleTrivyNotFound(err)
98-
return 2
123+
return "", err
99124
}
100125

101126
tmpFile, err := os.CreateTemp("", "codacy-sbom-*")
102127
if err != nil {
103128
logger.Error("Failed to create temp file", logrus.Fields{"error": err.Error()})
104129
color.Red("Error: Failed to create temporary file: %v", err)
105-
return 2
130+
return "", err
106131
}
107132
tmpFile.Close()
108133
sbomPath := tmpFile.Name()
109-
defer os.Remove(sbomPath)
110134

111135
fmt.Printf("Generating SBOM for image: %s\n", imageRef)
112136
args := []string{"image", "--format", sbomFormat, "-o", sbomPath, imageRef}
@@ -120,20 +144,11 @@ func executeUploadSBOM(imageRef string) int {
120144
color.Red("Error: Failed to generate SBOM: %v", err)
121145
}
122146
logger.Error("Trivy SBOM generation failed", logrus.Fields{"error": err.Error()})
123-
return 2
147+
os.Remove(sbomPath)
148+
return "", err
124149
}
125150
fmt.Println("SBOM generated successfully")
126-
127-
// Upload SBOM to Codacy
128-
fmt.Printf("Uploading SBOM to Codacy (org: %s/%s)...\n", sbomProvider, sbomOrg)
129-
if err := uploadSBOMToCodacy(sbomPath, sbomImageName, tag); err != nil {
130-
logger.Error("Failed to upload SBOM", logrus.Fields{"error": err.Error()})
131-
color.Red("Error: Failed to upload SBOM: %v", err)
132-
return 1
133-
}
134-
135-
color.Green("Successfully uploaded SBOM for %s:%s", sbomImageName, tag)
136-
return 0
151+
return sbomPath, nil
137152
}
138153

139154
// parseImageRef splits an image reference into name and tag.
@@ -162,38 +177,23 @@ func parseImageRef(imageRef string) (string, string) {
162177
return imageRef, "latest"
163178
}
164179

165-
func uploadSBOMToCodacy(sbomPath, imageName, tag string) error {
180+
type sbomUploadParams struct {
181+
provider string
182+
org string
183+
apiToken string
184+
repoName string
185+
env string
186+
}
187+
188+
func uploadSBOMToCodacy(sbomPath, imageName, tag string, params sbomUploadParams) error {
166189
url := fmt.Sprintf("https://app.codacy.com/api/v3/organizations/%s/%s/image-sboms",
167-
sbomProvider, sbomOrg)
190+
params.provider, params.org)
168191

169192
body := &bytes.Buffer{}
170193
writer := multipart.NewWriter(body)
171194

172-
// Add the SBOM file
173-
sbomFile, err := os.Open(sbomPath)
174-
if err != nil {
175-
return fmt.Errorf("failed to open SBOM file: %w", err)
176-
}
177-
defer sbomFile.Close()
178-
179-
part, err := writer.CreateFormFile("sbom", filepath.Base(sbomPath))
180-
if err != nil {
181-
return fmt.Errorf("failed to create form file: %w", err)
182-
}
183-
if _, err := io.Copy(part, sbomFile); err != nil {
184-
return fmt.Errorf("failed to write SBOM to form: %w", err)
185-
}
186-
187-
// Add required fields
188-
writer.WriteField("imageName", imageName)
189-
writer.WriteField("tag", tag)
190-
191-
// Add optional fields
192-
if sbomRepoName != "" {
193-
writer.WriteField("repositoryName", sbomRepoName)
194-
}
195-
if sbomEnv != "" {
196-
writer.WriteField("environment", sbomEnv)
195+
if err := buildSBOMMultipartForm(writer, sbomPath, imageName, tag, params); err != nil {
196+
return err
197197
}
198198

199199
if err := writer.Close(); err != nil {
@@ -206,7 +206,7 @@ func uploadSBOMToCodacy(sbomPath, imageName, tag string) error {
206206
}
207207
req.Header.Set("Content-Type", writer.FormDataContentType())
208208
req.Header.Set("Accept", "application/json")
209-
req.Header.Set("api-token", sbomAPIToken)
209+
req.Header.Set("api-token", params.apiToken)
210210

211211
resp, err := http.DefaultClient.Do(req)
212212
if err != nil {
@@ -221,3 +221,40 @@ func uploadSBOMToCodacy(sbomPath, imageName, tag string) error {
221221

222222
return nil
223223
}
224+
225+
// buildSBOMMultipartForm populates the multipart form with the SBOM file and metadata fields.
226+
func buildSBOMMultipartForm(writer *multipart.Writer, sbomPath, imageName, tag string, params sbomUploadParams) error {
227+
sbomFile, err := os.Open(sbomPath)
228+
if err != nil {
229+
return fmt.Errorf("failed to open SBOM file: %w", err)
230+
}
231+
defer sbomFile.Close()
232+
233+
part, err := writer.CreateFormFile("sbom", filepath.Base(sbomPath))
234+
if err != nil {
235+
return fmt.Errorf("failed to create form file: %w", err)
236+
}
237+
if _, err := io.Copy(part, sbomFile); err != nil {
238+
return fmt.Errorf("failed to write SBOM to form: %w", err)
239+
}
240+
241+
if err := writer.WriteField("imageName", imageName); err != nil {
242+
return fmt.Errorf("failed to write imageName field: %w", err)
243+
}
244+
if err := writer.WriteField("tag", tag); err != nil {
245+
return fmt.Errorf("failed to write tag field: %w", err)
246+
}
247+
248+
if params.repoName != "" {
249+
if err := writer.WriteField("repositoryName", params.repoName); err != nil {
250+
return fmt.Errorf("failed to write repositoryName field: %w", err)
251+
}
252+
}
253+
if params.env != "" {
254+
if err := writer.WriteField("environment", params.env); err != nil {
255+
return fmt.Errorf("failed to write environment field: %w", err)
256+
}
257+
}
258+
259+
return nil
260+
}

cmd/upload_sbom_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,12 @@ func TestExecuteUploadSBOM_TrivyCalledWithCorrectFormat(t *testing.T) {
183183
}
184184

185185
func TestUploadSBOMToCodacy_FileNotFound(t *testing.T) {
186-
err := uploadSBOMToCodacy("/nonexistent/file.json", "myapp", "latest")
186+
params := sbomUploadParams{
187+
provider: "gh",
188+
org: "test-org",
189+
apiToken: "test-token",
190+
}
191+
err := uploadSBOMToCodacy("/nonexistent/file.json", "myapp", "latest", params)
187192
assert.Error(t, err)
188193
assert.Contains(t, err.Error(), "failed to open SBOM file")
189194
}

integration-tests/init-with-token/expected/codacy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ runtimes:
55
tools:
66
- eslint@8.57.0
77
- lizard@1.17.31
8-
- opengrep@1.16.4
8+
- opengrep@1.17.0
99
- pmd@6.55.0
1010
- pylint@3.3.9
1111
- trivy@0.69.3

plugins/tools/trivy/test/expected.sarif

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2021-33203\nSeverity: MEDIUM\nFixed Version: 2.2.24, 3.1.12, 3.2.4\nLink: [CVE-2021-33203](https://avd.aquasec.com/nvd/cve-2021-33203)"
3535
},
3636
"ruleId": "CVE-2021-33203",
37-
"ruleIndex": 12
37+
"ruleIndex": 14
3838
},
3939
{
4040
"level": "error",
@@ -61,7 +61,7 @@
6161
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2022-36359\nSeverity: HIGH\nFixed Version: 3.2.15, 4.0.7\nLink: [CVE-2022-36359](https://avd.aquasec.com/nvd/cve-2022-36359)"
6262
},
6363
"ruleId": "CVE-2022-36359",
64-
"ruleIndex": 9
64+
"ruleIndex": 11
6565
},
6666
{
6767
"level": "error",
@@ -88,7 +88,7 @@
8888
"text": "Package: cross-spawn\nInstalled Version: 7.0.3\nVulnerability CVE-2024-21538\nSeverity: HIGH\nFixed Version: 7.0.5, 6.0.6\nLink: [CVE-2024-21538](https://avd.aquasec.com/nvd/cve-2024-21538)"
8989
},
9090
"ruleId": "CVE-2024-21538",
91-
"ruleIndex": 2
91+
"ruleIndex": 3
9292
},
9393
{
9494
"level": "warning",
@@ -115,7 +115,7 @@
115115
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2024-45231\nSeverity: MEDIUM\nFixed Version: 5.1.1, 5.0.9, 4.2.16\nLink: [CVE-2024-45231](https://avd.aquasec.com/nvd/cve-2024-45231)"
116116
},
117117
"ruleId": "CVE-2024-45231",
118-
"ruleIndex": 13
118+
"ruleIndex": 15
119119
},
120120
{
121121
"level": "warning",
@@ -142,7 +142,7 @@
142142
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2025-48432\nSeverity: MEDIUM\nFixed Version: 5.2.2, 5.1.10, 4.2.22\nLink: [CVE-2025-48432](https://avd.aquasec.com/nvd/cve-2025-48432)"
143143
},
144144
"ruleId": "CVE-2025-48432",
145-
"ruleIndex": 14
145+
"ruleIndex": 16
146146
},
147147
{
148148
"level": "error",
@@ -169,7 +169,7 @@
169169
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2025-57833\nSeverity: HIGH\nFixed Version: 4.2.24, 5.1.12, 5.2.6\nLink: [CVE-2025-57833](https://avd.aquasec.com/nvd/cve-2025-57833)"
170170
},
171171
"ruleId": "CVE-2025-57833",
172-
"ruleIndex": 10
172+
"ruleIndex": 12
173173
},
174174
{
175175
"level": "note",
@@ -196,7 +196,7 @@
196196
"text": "Package: brace-expansion\nInstalled Version: 1.1.11\nVulnerability CVE-2025-5889\nSeverity: LOW\nFixed Version: 2.0.2, 1.1.12, 3.0.1, 4.0.1\nLink: [CVE-2025-5889](https://avd.aquasec.com/nvd/cve-2025-5889)"
197197
},
198198
"ruleId": "CVE-2025-5889",
199-
"ruleIndex": 1
199+
"ruleIndex": 2
200200
},
201201
{
202202
"level": "error",
@@ -223,7 +223,7 @@
223223
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2025-64458\nSeverity: HIGH\nFixed Version: 5.2.8, 5.1.14, 4.2.26\nLink: [CVE-2025-64458](https://avd.aquasec.com/nvd/cve-2025-64458)"
224224
},
225225
"ruleId": "CVE-2025-64458",
226-
"ruleIndex": 11
226+
"ruleIndex": 13
227227
},
228228
{
229229
"level": "error",
@@ -250,7 +250,7 @@
250250
"text": "Package: django\nInstalled Version: 1.11.29\nVulnerability CVE-2025-64459\nSeverity: CRITICAL\nFixed Version: 5.2.8, 5.1.14, 4.2.26\nLink: [CVE-2025-64459](https://avd.aquasec.com/nvd/cve-2025-64459)"
251251
},
252252
"ruleId": "CVE-2025-64459",
253-
"ruleIndex": 8
253+
"ruleIndex": 10
254254
},
255255
{
256256
"level": "warning",
@@ -277,7 +277,7 @@
277277
"text": "Package: js-yaml\nInstalled Version: 4.1.0\nVulnerability CVE-2025-64718\nSeverity: MEDIUM\nFixed Version: 4.1.1, 3.14.2\nLink: [CVE-2025-64718](https://avd.aquasec.com/nvd/cve-2025-64718)"
278278
},
279279
"ruleId": "CVE-2025-64718",
280-
"ruleIndex": 4
280+
"ruleIndex": 6
281281
},
282282
{
283283
"level": "warning",
@@ -331,7 +331,7 @@
331331
"text": "Package: minimatch\nInstalled Version: 3.1.2\nVulnerability CVE-2026-26996\nSeverity: HIGH\nFixed Version: 10.2.1, 9.0.6, 8.0.5, 7.4.7, 6.2.1, 5.1.7, 4.2.4, 3.1.3\nLink: [CVE-2026-26996](https://avd.aquasec.com/nvd/cve-2026-26996)"
332332
},
333333
"ruleId": "CVE-2026-26996",
334-
"ruleIndex": 5
334+
"ruleIndex": 7
335335
},
336336
{
337337
"level": "error",
@@ -358,7 +358,7 @@
358358
"text": "Package: minimatch\nInstalled Version: 3.1.2\nVulnerability CVE-2026-27903\nSeverity: HIGH\nFixed Version: 10.2.3, 9.0.7, 8.0.6, 7.4.8, 6.2.2, 5.1.8, 4.2.5, 3.1.3\nLink: [CVE-2026-27903](https://avd.aquasec.com/nvd/cve-2026-27903)"
359359
},
360360
"ruleId": "CVE-2026-27903",
361-
"ruleIndex": 6
361+
"ruleIndex": 8
362362
},
363363
{
364364
"level": "error",
@@ -385,7 +385,7 @@
385385
"text": "Package: minimatch\nInstalled Version: 3.1.2\nVulnerability CVE-2026-27904\nSeverity: HIGH\nFixed Version: 10.2.3, 9.0.7, 8.0.6, 7.4.8, 6.2.2, 5.1.8, 4.2.5, 3.1.4\nLink: [CVE-2026-27904](https://avd.aquasec.com/nvd/cve-2026-27904)"
386386
},
387387
"ruleId": "CVE-2026-27904",
388-
"ruleIndex": 7
388+
"ruleIndex": 9
389389
},
390390
{
391391
"level": "error",
@@ -412,7 +412,61 @@
412412
"text": "Package: flatted\nInstalled Version: 3.3.1\nVulnerability CVE-2026-32141\nSeverity: HIGH\nFixed Version: 3.4.0\nLink: [CVE-2026-32141](https://avd.aquasec.com/nvd/cve-2026-32141)"
413413
},
414414
"ruleId": "CVE-2026-32141",
415-
"ruleIndex": 3
415+
"ruleIndex": 4
416+
},
417+
{
418+
"level": "error",
419+
"locations": [
420+
{
421+
"message": {
422+
"text": "package-lock.json: flatted@3.3.1"
423+
},
424+
"physicalLocation": {
425+
"artifactLocation": {
426+
"uri": "package-lock.json",
427+
"uriBaseId": "ROOTPATH"
428+
},
429+
"region": {
430+
"endColumn": 1,
431+
"endLine": 823,
432+
"startColumn": 1,
433+
"startLine": 819
434+
}
435+
}
436+
}
437+
],
438+
"message": {
439+
"text": "Package: flatted\nInstalled Version: 3.3.1\nVulnerability CVE-2026-33228\nSeverity: HIGH\nFixed Version: 3.4.2\nLink: [CVE-2026-33228](https://avd.aquasec.com/nvd/cve-2026-33228)"
440+
},
441+
"ruleId": "CVE-2026-33228",
442+
"ruleIndex": 5
443+
},
444+
{
445+
"level": "warning",
446+
"locations": [
447+
{
448+
"message": {
449+
"text": "package-lock.json: brace-expansion@1.1.11"
450+
},
451+
"physicalLocation": {
452+
"artifactLocation": {
453+
"uri": "package-lock.json",
454+
"uriBaseId": "ROOTPATH"
455+
},
456+
"region": {
457+
"endColumn": 1,
458+
"endLine": 357,
459+
"startColumn": 1,
460+
"startLine": 349
461+
}
462+
}
463+
}
464+
],
465+
"message": {
466+
"text": "Package: brace-expansion\nInstalled Version: 1.1.11\nVulnerability CVE-2026-33750\nSeverity: MEDIUM\nFixed Version: 5.0.5, 3.0.2, 2.0.3, 1.1.13\nLink: [CVE-2026-33750](https://avd.aquasec.com/nvd/cve-2026-33750)"
467+
},
468+
"ruleId": "CVE-2026-33750",
469+
"ruleIndex": 1
416470
}
417471
],
418472
"tool": {

0 commit comments

Comments
 (0)