Support multiple root paths when generating the Sonarqube report

Signed-off-by: Cosmin Cojocar <cosmin.cojocar@gmx.ch>
This commit is contained in:
Cosmin Cojocar 2019-06-24 14:35:11 +02:00
parent 46e55b908d
commit 020479a832
3 changed files with 134 additions and 18 deletions

View file

@ -171,19 +171,27 @@ func loadRules(include, exclude string) rules.RuleList {
return rules.Generate(filters...) return rules.Generate(filters...)
} }
func saveOutput(filename, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error { func saveOutput(filename, format string, paths []string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
rootPaths := []string{}
for _, path := range paths {
rootPath, err := gosec.RootPath(path)
if err != nil {
return fmt.Errorf("failed to get the root path of the projects: %s", err)
}
rootPaths = append(rootPaths, rootPath)
}
if filename != "" { if filename != "" {
outfile, err := os.Create(filename) outfile, err := os.Create(filename)
if err != nil { if err != nil {
return err return err
} }
defer outfile.Close() defer outfile.Close()
err = output.CreateReport(outfile, format, rootPath, issues, metrics, errors) err = output.CreateReport(outfile, format, rootPaths, issues, metrics, errors)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
err := output.CreateReport(os.Stdout, format, rootPath, issues, metrics, errors) err := output.CreateReport(os.Stdout, format, rootPaths, issues, metrics, errors)
if err != nil { if err != nil {
return err return err
} }
@ -318,13 +326,8 @@ func main() {
os.Exit(0) os.Exit(0)
} }
rootPath, err := gosec.RootPath(flag.Args()[0])
if err != nil {
logger.Fatalf("Failed to get the root path of the project: %s", err)
}
// Create output report // Create output report
if err := saveOutput(*flagOutput, *flagFormat, rootPath, issues, metrics, errors); err != nil { if err := saveOutput(*flagOutput, *flagFormat, flag.Args(), issues, metrics, errors); err != nil {
logger.Fatal(err) logger.Fatal(err)
} }

View file

@ -76,7 +76,7 @@ type reportInfo struct {
// CreateReport generates a report based for the supplied issues and metrics given // CreateReport generates a report based for the supplied issues and metrics given
// the specified format. The formats currently accepted are: json, csv, html and text. // the specified format. The formats currently accepted are: json, csv, html and text.
func CreateReport(w io.Writer, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error { func CreateReport(w io.Writer, format string, rootPaths []string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
data := &reportInfo{ data := &reportInfo{
Errors: errors, Errors: errors,
Issues: issues, Issues: issues,
@ -97,15 +97,15 @@ func CreateReport(w io.Writer, format, rootPath string, issues []*gosec.Issue, m
case "text": case "text":
err = reportFromPlaintextTemplate(w, text, data) err = reportFromPlaintextTemplate(w, text, data)
case "sonarqube": case "sonarqube":
err = reportSonarqube(rootPath, w, data) err = reportSonarqube(rootPaths, w, data)
default: default:
err = reportFromPlaintextTemplate(w, text, data) err = reportFromPlaintextTemplate(w, text, data)
} }
return err return err
} }
func reportSonarqube(rootPath string, w io.Writer, data *reportInfo) error { func reportSonarqube(rootPaths []string, w io.Writer, data *reportInfo) error {
si, err := convertToSonarIssues(rootPath, data) si, err := convertToSonarIssues(rootPaths, data)
if err != nil { if err != nil {
return err return err
} }
@ -117,11 +117,20 @@ func reportSonarqube(rootPath string, w io.Writer, data *reportInfo) error {
return err return err
} }
func convertToSonarIssues(rootPath string, data *reportInfo) (sonarIssues, error) { func convertToSonarIssues(rootPaths []string, data *reportInfo) (sonarIssues, error) {
var si sonarIssues var si sonarIssues
for _, issue := range data.Issues { for _, issue := range data.Issues {
lines := strings.Split(issue.Line, "-") var sonarFilePath string
for _, rootPath := range rootPaths {
if strings.HasPrefix(issue.File, rootPath) {
sonarFilePath = strings.Replace(issue.File, rootPath+"/", "", 1)
}
}
if sonarFilePath == "" {
continue
}
lines := strings.Split(issue.Line, "-")
startLine, err := strconv.Atoi(lines[0]) startLine, err := strconv.Atoi(lines[0])
if err != nil { if err != nil {
return si, err return si, err
@ -133,12 +142,13 @@ func convertToSonarIssues(rootPath string, data *reportInfo) (sonarIssues, error
return si, err return si, err
} }
} }
s := sonarIssue{ s := sonarIssue{
EngineID: "gosec", EngineID: "gosec",
RuleID: issue.RuleID, RuleID: issue.RuleID,
PrimaryLocation: location{ PrimaryLocation: location{
Message: issue.What, Message: issue.What,
FilePath: strings.Replace(issue.File, rootPath+"/", "", 1), FilePath: sonarFilePath,
TextRange: textRange{StartLine: startLine, EndLine: endLine}, TextRange: textRange{StartLine: startLine, EndLine: endLine},
}, },
Type: "VULNERABILITY", Type: "VULNERABILITY",

View file

@ -54,7 +54,7 @@ var _ = Describe("Formatter", func() {
rootPath := "/home/src/project" rootPath := "/home/src/project"
issues, err := convertToSonarIssues(rootPath, data) issues, err := convertToSonarIssues([]string{rootPath}, data)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
Expect(issues).To(Equal(want)) Expect(issues).To(Equal(want))
}) })
@ -102,7 +102,110 @@ var _ = Describe("Formatter", func() {
rootPath := "/home/src/project" rootPath := "/home/src/project"
issues, err := convertToSonarIssues(rootPath, data) issues, err := convertToSonarIssues([]string{rootPath}, data)
Expect(err).ShouldNot(HaveOccurred())
Expect(issues).To(Equal(want))
})
It("it should not parse the report info for files from other projects", func() {
data := &reportInfo{
Errors: map[string][]gosec.Error{},
Issues: []*gosec.Issue{
&gosec.Issue{
Severity: 2,
Confidence: 0,
RuleID: "test",
What: "test",
File: "/home/src/project1/test.go",
Code: "",
Line: "1-2",
},
},
Stats: &gosec.Metrics{
NumFiles: 0,
NumLines: 0,
NumNosec: 0,
NumFound: 0,
},
}
want := sonarIssues{
SonarIssues: nil,
}
rootPath := "/home/src/project2"
issues, err := convertToSonarIssues([]string{rootPath}, data)
Expect(err).ShouldNot(HaveOccurred())
Expect(issues).To(Equal(want))
})
It("it should parse the report info for multiple projects projects", func() {
data := &reportInfo{
Errors: map[string][]gosec.Error{},
Issues: []*gosec.Issue{
&gosec.Issue{
Severity: 2,
Confidence: 0,
RuleID: "test",
What: "test",
File: "/home/src/project1/test-project1.go",
Code: "",
Line: "1-2",
},
&gosec.Issue{
Severity: 2,
Confidence: 0,
RuleID: "test",
What: "test",
File: "/home/src/project2/test-project2.go",
Code: "",
Line: "1-2",
},
},
Stats: &gosec.Metrics{
NumFiles: 0,
NumLines: 0,
NumNosec: 0,
NumFound: 0,
},
}
want := sonarIssues{
SonarIssues: []sonarIssue{
{
EngineID: "gosec",
RuleID: "test",
PrimaryLocation: location{
Message: "test",
FilePath: "test-project1.go",
TextRange: textRange{
StartLine: 1,
EndLine: 2,
},
},
Type: "VULNERABILITY",
Severity: "BLOCKER",
EffortMinutes: SonarqubeEffortMinutes,
},
{
EngineID: "gosec",
RuleID: "test",
PrimaryLocation: location{
Message: "test",
FilePath: "test-project2.go",
TextRange: textRange{
StartLine: 1,
EndLine: 2,
},
},
Type: "VULNERABILITY",
Severity: "BLOCKER",
EffortMinutes: SonarqubeEffortMinutes,
},
},
}
rootPaths := []string{"/home/src/project1", "/home/src/project2"}
issues, err := convertToSonarIssues(rootPaths, data)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
Expect(issues).To(Equal(want)) Expect(issues).To(Equal(want))
}) })