Skip to content

Commit 1700ee6

Browse files
refactor: move SARIF merging logic to utils package
- Moved the `mergeSarifOutputs` function from `analyze.go` to `utils/sarif.go` for better code organization. - Updated the `analyze.go` file to use the new utility function for merging SARIF outputs. - Enhanced the `Rule` struct in the utils package to use `map[string]interface{}` for properties.
1 parent d31122d commit 1700ee6

3 files changed

Lines changed: 171 additions & 83 deletions

File tree

cmd/analyze.go

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"os"
1212
"path/filepath"
1313

14+
"codacy/cli-v2/utils"
15+
1416
"github.com/spf13/cobra"
1517
)
1618

@@ -265,7 +267,7 @@ var analyzeCmd = &cobra.Command{
265267
}
266268

267269
// Merge all SARIF outputs
268-
if err := mergeSarifOutputs(sarifOutputs, outputFile); err != nil {
270+
if err := utils.MergeSarifOutputs(sarifOutputs, outputFile); err != nil {
269271
log.Fatalf("Failed to merge SARIF outputs: %v", err)
270272
}
271273
} else {
@@ -292,82 +294,3 @@ func runTool(workDirectory string, toolName string, args []string, outputFile st
292294
log.Printf("Warning: Unsupported tool: %s\n", toolName)
293295
}
294296
}
295-
296-
func mergeSarifOutputs(inputFiles []string, outputFile string) error {
297-
var mergedSarif Sarif
298-
mergedSarif.Runs = make([]struct {
299-
Tool struct {
300-
Driver struct {
301-
Name string `json:"name"`
302-
Version string `json:"version"`
303-
Rules []struct {
304-
ID string `json:"id"`
305-
HelpURI string `json:"helpUri"`
306-
ShortDescription struct {
307-
Text string `json:"text"`
308-
} `json:"shortDescription"`
309-
} `json:"rules"`
310-
} `json:"driver"`
311-
} `json:"tool"`
312-
Artifacts []struct {
313-
Location struct {
314-
URI string `json:"uri"`
315-
} `json:"location"`
316-
} `json:"artifacts"`
317-
Results []struct {
318-
Level string `json:"level"`
319-
Message struct {
320-
Text string `json:"text"`
321-
} `json:"message"`
322-
Locations []struct {
323-
PhysicalLocation struct {
324-
ArtifactLocation struct {
325-
URI string `json:"uri"`
326-
Index int `json:"index"`
327-
} `json:"artifactLocation"`
328-
Region struct {
329-
StartLine int `json:"startLine"`
330-
StartColumn int `json:"startColumn"`
331-
EndLine int `json:"endLine"`
332-
EndColumn int `json:"endColumn"`
333-
} `json:"region"`
334-
} `json:"physicalLocation"`
335-
} `json:"locations"`
336-
RuleID string `json:"ruleId"`
337-
RuleIndex int `json:"ruleIndex"`
338-
} `json:"results"`
339-
}, 0)
340-
341-
for _, file := range inputFiles {
342-
data, err := os.ReadFile(file)
343-
if err != nil {
344-
if os.IsNotExist(err) {
345-
// Skip if file doesn't exist (tool might have failed)
346-
continue
347-
}
348-
return fmt.Errorf("failed to read SARIF file %s: %w", file, err)
349-
}
350-
351-
var sarif Sarif
352-
if err := json.Unmarshal(data, &sarif); err != nil {
353-
return fmt.Errorf("failed to parse SARIF file %s: %w", file, err)
354-
}
355-
356-
mergedSarif.Runs = append(mergedSarif.Runs, sarif.Runs...)
357-
}
358-
359-
// Create output file
360-
out, err := os.Create(outputFile)
361-
if err != nil {
362-
return fmt.Errorf("failed to create output file: %w", err)
363-
}
364-
defer out.Close()
365-
366-
encoder := json.NewEncoder(out)
367-
encoder.SetIndent("", " ")
368-
if err := encoder.Encode(mergedSarif); err != nil {
369-
return fmt.Errorf("failed to write merged SARIF: %w", err)
370-
}
371-
372-
return nil
373-
}

results.sarif

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"version": "2.1.0",
3+
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
4+
"runs": [
5+
{
6+
"tool": {
7+
"driver": {
8+
"name": "Trivy",
9+
"version": "0.59.1",
10+
"informationUri": "https://github.com/aquasecurity/trivy",
11+
"rules": [
12+
{
13+
"id": "CVE-2024-21538",
14+
"shortDescription": {
15+
"text": "cross-spawn: regular expression denial of service"
16+
},
17+
"properties": {
18+
"precision": "very-high",
19+
"security-severity": "7.5",
20+
"tags": [
21+
"vulnerability",
22+
"security",
23+
"HIGH"
24+
]
25+
}
26+
}
27+
]
28+
}
29+
},
30+
"results": [
31+
{
32+
"ruleId": "CVE-2024-21538",
33+
"level": "error",
34+
"message": {
35+
"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)"
36+
},
37+
"locations": [
38+
{
39+
"physicalLocation": {
40+
"artifactLocation": {
41+
"uri": "tools/testdata/repositories/trivy/src/package-lock.json"
42+
},
43+
"region": {
44+
"startLine": 515,
45+
"startColumn": 1
46+
}
47+
}
48+
}
49+
]
50+
}
51+
]
52+
},
53+
{
54+
"tool": {
55+
"driver": {
56+
"name": "Pylint",
57+
"version": "3.3.6",
58+
"informationUri": "https://pylint.org",
59+
"rules": null
60+
}
61+
},
62+
"results": []
63+
},
64+
{
65+
"tool": {
66+
"driver": {
67+
"name": "PMD",
68+
"version": "6.55.0",
69+
"informationUri": "https://pmd.github.io/pmd/",
70+
"rules": [
71+
{
72+
"id": "UnusedPrivateField",
73+
"shortDescription": {
74+
"text": "Avoid unused private fields such as 'x'."
75+
},
76+
"properties": {
77+
"priority": 3,
78+
"ruleset": "Best Practices",
79+
"tags": [
80+
"Best Practices"
81+
]
82+
}
83+
}
84+
]
85+
}
86+
},
87+
"results": [
88+
{
89+
"ruleId": "UnusedPrivateField",
90+
"level": "",
91+
"message": {
92+
"text": "Avoid unused private fields such as 'x'."
93+
},
94+
"locations": [
95+
{
96+
"physicalLocation": {
97+
"artifactLocation": {
98+
"uri": "/Users/czak/GIT/codacy/codacy-cli-v2/tools/testdata/repositories/pmd/RulesBreaker.java"
99+
},
100+
"region": {
101+
"startLine": 18,
102+
"startColumn": 17
103+
}
104+
}
105+
}
106+
]
107+
}
108+
]
109+
},
110+
{
111+
"tool": {
112+
"driver": {
113+
"name": "ESLint",
114+
"version": "9.3.0",
115+
"informationUri": "https://eslint.org",
116+
"rules": []
117+
}
118+
},
119+
"results": []
120+
}
121+
]
122+
}

utils/sarif.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package utils
22

33
import (
44
"encoding/json"
5+
"fmt"
6+
"os"
57
)
68

79
// PylintIssue represents a single issue in Pylint's JSON output
@@ -41,9 +43,9 @@ type Driver struct {
4143
}
4244

4345
type Rule struct {
44-
ID string `json:"id"`
45-
ShortDescription MessageText `json:"shortDescription"`
46-
Properties map[string]string `json:"properties"`
46+
ID string `json:"id"`
47+
ShortDescription MessageText `json:"shortDescription"`
48+
Properties map[string]interface{} `json:"properties"`
4749
}
4850

4951
type Result struct {
@@ -169,3 +171,44 @@ func createEmptySarifReport() []byte {
169171
sarifData, _ := json.MarshalIndent(emptyReport, "", " ")
170172
return sarifData
171173
}
174+
175+
// MergeSarifOutputs combines multiple SARIF files into a single output file
176+
func MergeSarifOutputs(inputFiles []string, outputFile string) error {
177+
var mergedSarif SarifReport
178+
mergedSarif.Version = "2.1.0"
179+
mergedSarif.Schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
180+
mergedSarif.Runs = make([]Run, 0)
181+
182+
for _, file := range inputFiles {
183+
data, err := os.ReadFile(file)
184+
if err != nil {
185+
if os.IsNotExist(err) {
186+
// Skip if file doesn't exist (tool might have failed)
187+
continue
188+
}
189+
return fmt.Errorf("failed to read SARIF file %s: %w", file, err)
190+
}
191+
192+
var sarif SarifReport
193+
if err := json.Unmarshal(data, &sarif); err != nil {
194+
return fmt.Errorf("failed to parse SARIF file %s: %w", file, err)
195+
}
196+
197+
mergedSarif.Runs = append(mergedSarif.Runs, sarif.Runs...)
198+
}
199+
200+
// Create output file
201+
out, err := os.Create(outputFile)
202+
if err != nil {
203+
return fmt.Errorf("failed to create output file: %w", err)
204+
}
205+
defer out.Close()
206+
207+
encoder := json.NewEncoder(out)
208+
encoder.SetIndent("", " ")
209+
if err := encoder.Encode(mergedSarif); err != nil {
210+
return fmt.Errorf("failed to write merged SARIF: %w", err)
211+
}
212+
213+
return nil
214+
}

0 commit comments

Comments
 (0)