mirror of
https://github.com/securego/gosec.git
synced 2025-01-03 22:52:22 +02:00
Merge pull request #328 from ccojocar/fix-sonarqute-report
Fix the file path in the Sonarqube report and also add support for multiple root folders
This commit is contained in:
commit
36a82ea85e
@ -171,19 +171,27 @@ func loadRules(include, exclude string) rules.RuleList {
|
||||
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 != "" {
|
||||
outfile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outfile.Close()
|
||||
err = output.CreateReport(outfile, format, rootPath, issues, metrics, errors)
|
||||
err = output.CreateReport(outfile, format, rootPaths, issues, metrics, errors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := output.CreateReport(os.Stdout, format, rootPath, issues, metrics, errors)
|
||||
err := output.CreateReport(os.Stdout, format, rootPaths, issues, metrics, errors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -318,9 +326,8 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
rootPath := packages[0]
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -387,3 +387,11 @@ func PackagePaths(root string, exclude *regexp.Regexp) ([]string, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RootPath returns the absolute root path of a scan
|
||||
func RootPath(root string) (string, error) {
|
||||
if strings.HasSuffix(root, "...") {
|
||||
root = root[0 : len(root)-3]
|
||||
}
|
||||
return filepath.Abs(root)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package gosec_test
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@ -53,4 +54,23 @@ var _ = Describe("Helpers", func() {
|
||||
Expect(paths).Should(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when getting the root path", func() {
|
||||
It("should return the absolute path from relative path", func() {
|
||||
base := "test"
|
||||
cwd, err := os.Getwd()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
root, err := gosec.RootPath(base)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(root).Should(Equal(filepath.Join(cwd, base)))
|
||||
})
|
||||
It("should retrun the absolute path from ellipsis path", func() {
|
||||
base := "test"
|
||||
cwd, err := os.Getwd()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
root, err := gosec.RootPath(filepath.Join(base, "..."))
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(root).Should(Equal(filepath.Join(cwd, base)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -76,7 +76,7 @@ type reportInfo struct {
|
||||
|
||||
// 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.
|
||||
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{
|
||||
Errors: errors,
|
||||
Issues: issues,
|
||||
@ -97,35 +97,58 @@ func CreateReport(w io.Writer, format, rootPath string, issues []*gosec.Issue, m
|
||||
case "text":
|
||||
err = reportFromPlaintextTemplate(w, text, data)
|
||||
case "sonarqube":
|
||||
err = reportSonarqube(rootPath, w, data)
|
||||
err = reportSonarqube(rootPaths, w, data)
|
||||
default:
|
||||
err = reportFromPlaintextTemplate(w, text, data)
|
||||
}
|
||||
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(rootPaths, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := json.MarshalIndent(si, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(raw)
|
||||
return err
|
||||
}
|
||||
|
||||
func convertToSonarIssues(rootPaths []string, data *reportInfo) (sonarIssues, error) {
|
||||
var si sonarIssues
|
||||
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])
|
||||
if err != nil {
|
||||
return err
|
||||
return si, err
|
||||
}
|
||||
endLine := startLine
|
||||
if len(lines) > 1 {
|
||||
endLine, err = strconv.Atoi(lines[1])
|
||||
if err != nil {
|
||||
return err
|
||||
return si, err
|
||||
}
|
||||
}
|
||||
|
||||
s := sonarIssue{
|
||||
EngineID: "gosec",
|
||||
RuleID: issue.RuleID,
|
||||
PrimaryLocation: location{
|
||||
Message: issue.What,
|
||||
FilePath: strings.Replace(issue.File, rootPath+"/", "", 1),
|
||||
FilePath: sonarFilePath,
|
||||
TextRange: textRange{StartLine: startLine, EndLine: endLine},
|
||||
},
|
||||
Type: "VULNERABILITY",
|
||||
@ -134,12 +157,7 @@ func reportSonarqube(rootPath string, w io.Writer, data *reportInfo) error {
|
||||
}
|
||||
si.SonarIssues = append(si.SonarIssues, s)
|
||||
}
|
||||
raw, err := json.MarshalIndent(si, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(raw)
|
||||
return err
|
||||
return si, nil
|
||||
}
|
||||
|
||||
func reportJSON(w io.Writer, data *reportInfo) error {
|
||||
|
13
output/formatter_suite_test.go
Normal file
13
output/formatter_suite_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRules(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Formatters Suite")
|
||||
}
|
213
output/formatter_test.go
Normal file
213
output/formatter_test.go
Normal file
@ -0,0 +1,213 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
var _ = Describe("Formatter", func() {
|
||||
BeforeEach(func() {
|
||||
})
|
||||
Context("when converting to Sonarqube issues", func() {
|
||||
It("it should parse the report info", func() {
|
||||
data := &reportInfo{
|
||||
Errors: map[string][]gosec.Error{},
|
||||
Issues: []*gosec.Issue{
|
||||
&gosec.Issue{
|
||||
Severity: 2,
|
||||
Confidence: 0,
|
||||
RuleID: "test",
|
||||
What: "test",
|
||||
File: "/home/src/project/test.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.go",
|
||||
TextRange: textRange{
|
||||
StartLine: 1,
|
||||
EndLine: 2,
|
||||
},
|
||||
},
|
||||
Type: "VULNERABILITY",
|
||||
Severity: "BLOCKER",
|
||||
EffortMinutes: SonarqubeEffortMinutes,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rootPath := "/home/src/project"
|
||||
|
||||
issues, err := convertToSonarIssues([]string{rootPath}, data)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(issues).To(Equal(want))
|
||||
})
|
||||
|
||||
It("it should parse the report info with files in subfolders", func() {
|
||||
data := &reportInfo{
|
||||
Errors: map[string][]gosec.Error{},
|
||||
Issues: []*gosec.Issue{
|
||||
&gosec.Issue{
|
||||
Severity: 2,
|
||||
Confidence: 0,
|
||||
RuleID: "test",
|
||||
What: "test",
|
||||
File: "/home/src/project/subfolder/test.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: "subfolder/test.go",
|
||||
TextRange: textRange{
|
||||
StartLine: 1,
|
||||
EndLine: 2,
|
||||
},
|
||||
},
|
||||
Type: "VULNERABILITY",
|
||||
Severity: "BLOCKER",
|
||||
EffortMinutes: SonarqubeEffortMinutes,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rootPath := "/home/src/project"
|
||||
|
||||
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(issues).To(Equal(want))
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user