Skip to content

Commit 020479a

Browse files
committed
Support multiple root paths when generating the Sonarqube report
Signed-off-by: Cosmin Cojocar <[email protected]>
1 parent 46e55b9 commit 020479a

File tree

3 files changed

+134
-18
lines changed

3 files changed

+134
-18
lines changed

cmd/gosec/main.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,19 +171,27 @@ func loadRules(include, exclude string) rules.RuleList {
171171
return rules.Generate(filters...)
172172
}
173173

174-
func saveOutput(filename, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
174+
func saveOutput(filename, format string, paths []string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
175+
rootPaths := []string{}
176+
for _, path := range paths {
177+
rootPath, err := gosec.RootPath(path)
178+
if err != nil {
179+
return fmt.Errorf("failed to get the root path of the projects: %s", err)
180+
}
181+
rootPaths = append(rootPaths, rootPath)
182+
}
175183
if filename != "" {
176184
outfile, err := os.Create(filename)
177185
if err != nil {
178186
return err
179187
}
180188
defer outfile.Close()
181-
err = output.CreateReport(outfile, format, rootPath, issues, metrics, errors)
189+
err = output.CreateReport(outfile, format, rootPaths, issues, metrics, errors)
182190
if err != nil {
183191
return err
184192
}
185193
} else {
186-
err := output.CreateReport(os.Stdout, format, rootPath, issues, metrics, errors)
194+
err := output.CreateReport(os.Stdout, format, rootPaths, issues, metrics, errors)
187195
if err != nil {
188196
return err
189197
}
@@ -318,13 +326,8 @@ func main() {
318326
os.Exit(0)
319327
}
320328

321-
rootPath, err := gosec.RootPath(flag.Args()[0])
322-
if err != nil {
323-
logger.Fatalf("Failed to get the root path of the project: %s", err)
324-
}
325-
326329
// Create output report
327-
if err := saveOutput(*flagOutput, *flagFormat, rootPath, issues, metrics, errors); err != nil {
330+
if err := saveOutput(*flagOutput, *flagFormat, flag.Args(), issues, metrics, errors); err != nil {
328331
logger.Fatal(err)
329332
}
330333

output/formatter.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ type reportInfo struct {
7676

7777
// CreateReport generates a report based for the supplied issues and metrics given
7878
// the specified format. The formats currently accepted are: json, csv, html and text.
79-
func CreateReport(w io.Writer, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
79+
func CreateReport(w io.Writer, format string, rootPaths []string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
8080
data := &reportInfo{
8181
Errors: errors,
8282
Issues: issues,
@@ -97,15 +97,15 @@ func CreateReport(w io.Writer, format, rootPath string, issues []*gosec.Issue, m
9797
case "text":
9898
err = reportFromPlaintextTemplate(w, text, data)
9999
case "sonarqube":
100-
err = reportSonarqube(rootPath, w, data)
100+
err = reportSonarqube(rootPaths, w, data)
101101
default:
102102
err = reportFromPlaintextTemplate(w, text, data)
103103
}
104104
return err
105105
}
106106

107-
func reportSonarqube(rootPath string, w io.Writer, data *reportInfo) error {
108-
si, err := convertToSonarIssues(rootPath, data)
107+
func reportSonarqube(rootPaths []string, w io.Writer, data *reportInfo) error {
108+
si, err := convertToSonarIssues(rootPaths, data)
109109
if err != nil {
110110
return err
111111
}
@@ -117,11 +117,20 @@ func reportSonarqube(rootPath string, w io.Writer, data *reportInfo) error {
117117
return err
118118
}
119119

120-
func convertToSonarIssues(rootPath string, data *reportInfo) (sonarIssues, error) {
120+
func convertToSonarIssues(rootPaths []string, data *reportInfo) (sonarIssues, error) {
121121
var si sonarIssues
122122
for _, issue := range data.Issues {
123-
lines := strings.Split(issue.Line, "-")
123+
var sonarFilePath string
124+
for _, rootPath := range rootPaths {
125+
if strings.HasPrefix(issue.File, rootPath) {
126+
sonarFilePath = strings.Replace(issue.File, rootPath+"/", "", 1)
127+
}
128+
}
129+
if sonarFilePath == "" {
130+
continue
131+
}
124132

133+
lines := strings.Split(issue.Line, "-")
125134
startLine, err := strconv.Atoi(lines[0])
126135
if err != nil {
127136
return si, err
@@ -133,12 +142,13 @@ func convertToSonarIssues(rootPath string, data *reportInfo) (sonarIssues, error
133142
return si, err
134143
}
135144
}
145+
136146
s := sonarIssue{
137147
EngineID: "gosec",
138148
RuleID: issue.RuleID,
139149
PrimaryLocation: location{
140150
Message: issue.What,
141-
FilePath: strings.Replace(issue.File, rootPath+"/", "", 1),
151+
FilePath: sonarFilePath,
142152
TextRange: textRange{StartLine: startLine, EndLine: endLine},
143153
},
144154
Type: "VULNERABILITY",

output/formatter_test.go

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var _ = Describe("Formatter", func() {
5454

5555
rootPath := "/home/src/project"
5656

57-
issues, err := convertToSonarIssues(rootPath, data)
57+
issues, err := convertToSonarIssues([]string{rootPath}, data)
5858
Expect(err).ShouldNot(HaveOccurred())
5959
Expect(issues).To(Equal(want))
6060
})
@@ -102,7 +102,110 @@ var _ = Describe("Formatter", func() {
102102

103103
rootPath := "/home/src/project"
104104

105-
issues, err := convertToSonarIssues(rootPath, data)
105+
issues, err := convertToSonarIssues([]string{rootPath}, data)
106+
Expect(err).ShouldNot(HaveOccurred())
107+
Expect(issues).To(Equal(want))
108+
})
109+
It("it should not parse the report info for files from other projects", func() {
110+
data := &reportInfo{
111+
Errors: map[string][]gosec.Error{},
112+
Issues: []*gosec.Issue{
113+
&gosec.Issue{
114+
Severity: 2,
115+
Confidence: 0,
116+
RuleID: "test",
117+
What: "test",
118+
File: "/home/src/project1/test.go",
119+
Code: "",
120+
Line: "1-2",
121+
},
122+
},
123+
Stats: &gosec.Metrics{
124+
NumFiles: 0,
125+
NumLines: 0,
126+
NumNosec: 0,
127+
NumFound: 0,
128+
},
129+
}
130+
want := sonarIssues{
131+
SonarIssues: nil,
132+
}
133+
134+
rootPath := "/home/src/project2"
135+
136+
issues, err := convertToSonarIssues([]string{rootPath}, data)
137+
Expect(err).ShouldNot(HaveOccurred())
138+
Expect(issues).To(Equal(want))
139+
})
140+
141+
It("it should parse the report info for multiple projects projects", func() {
142+
data := &reportInfo{
143+
Errors: map[string][]gosec.Error{},
144+
Issues: []*gosec.Issue{
145+
&gosec.Issue{
146+
Severity: 2,
147+
Confidence: 0,
148+
RuleID: "test",
149+
What: "test",
150+
File: "/home/src/project1/test-project1.go",
151+
Code: "",
152+
Line: "1-2",
153+
},
154+
&gosec.Issue{
155+
Severity: 2,
156+
Confidence: 0,
157+
RuleID: "test",
158+
What: "test",
159+
File: "/home/src/project2/test-project2.go",
160+
Code: "",
161+
Line: "1-2",
162+
},
163+
},
164+
Stats: &gosec.Metrics{
165+
NumFiles: 0,
166+
NumLines: 0,
167+
NumNosec: 0,
168+
NumFound: 0,
169+
},
170+
}
171+
want := sonarIssues{
172+
SonarIssues: []sonarIssue{
173+
{
174+
EngineID: "gosec",
175+
RuleID: "test",
176+
PrimaryLocation: location{
177+
Message: "test",
178+
FilePath: "test-project1.go",
179+
TextRange: textRange{
180+
StartLine: 1,
181+
EndLine: 2,
182+
},
183+
},
184+
Type: "VULNERABILITY",
185+
Severity: "BLOCKER",
186+
EffortMinutes: SonarqubeEffortMinutes,
187+
},
188+
{
189+
EngineID: "gosec",
190+
RuleID: "test",
191+
PrimaryLocation: location{
192+
Message: "test",
193+
FilePath: "test-project2.go",
194+
TextRange: textRange{
195+
StartLine: 1,
196+
EndLine: 2,
197+
},
198+
},
199+
Type: "VULNERABILITY",
200+
Severity: "BLOCKER",
201+
EffortMinutes: SonarqubeEffortMinutes,
202+
},
203+
},
204+
}
205+
206+
rootPaths := []string{"/home/src/project1", "/home/src/project2"}
207+
208+
issues, err := convertToSonarIssues(rootPaths, data)
106209
Expect(err).ShouldNot(HaveOccurred())
107210
Expect(issues).To(Equal(want))
108211
})

0 commit comments

Comments
 (0)