Skip to content

Commit ddfe54d

Browse files
kmcrawfordgcmurphy
authored andcommitted
Add sonarqube output
1 parent c5e6c4a commit ddfe54d

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

cmd/gosec/main.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var (
6565
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
6666

6767
// format output
68-
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, or text")
68+
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, or text")
6969

7070
// output file
7171
flagOutput = flag.String("out", "", "Set output file for results")
@@ -165,19 +165,19 @@ func loadRules(include, exclude string) rules.RuleList {
165165
return rules.Generate(filters...)
166166
}
167167

168-
func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
168+
func saveOutput(filename, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
169169
if filename != "" {
170170
outfile, err := os.Create(filename)
171171
if err != nil {
172172
return err
173173
}
174174
defer outfile.Close()
175-
err = output.CreateReport(outfile, format, issues, metrics, errors)
175+
err = output.CreateReport(outfile, format, rootPath, issues, metrics, errors)
176176
if err != nil {
177177
return err
178178
}
179179
} else {
180-
err := output.CreateReport(os.Stdout, format, issues, metrics, errors)
180+
err := output.CreateReport(os.Stdout, format, rootPath, issues, metrics, errors)
181181
if err != nil {
182182
return err
183183
}
@@ -318,6 +318,7 @@ func main() {
318318
if *flagBuildTags != "" {
319319
buildTags = strings.Split(*flagBuildTags, ",")
320320
}
321+
321322
if err := analyzer.Process(buildTags, packages...); err != nil {
322323
logger.Fatal(err)
323324
}
@@ -342,9 +343,9 @@ func main() {
342343
if !issuesFound && *flagQuiet {
343344
os.Exit(0)
344345
}
345-
346+
rootPath := packages[0]
346347
// Create output report
347-
if err := saveOutput(*flagOutput, *flagFormat, issues, metrics, errors); err != nil {
348+
if err := saveOutput(*flagOutput, *flagFormat, rootPath, issues, metrics, errors); err != nil {
348349
logger.Fatal(err)
349350
}
350351

output/formatter.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"encoding/xml"
2121
htmlTemplate "html/template"
2222
"io"
23+
"strconv"
24+
"strings"
2325
plainTemplate "text/template"
2426

2527
"github.com/securego/gosec"
@@ -71,7 +73,7 @@ type reportInfo struct {
7173

7274
// CreateReport generates a report based for the supplied issues and metrics given
7375
// the specified format. The formats currently accepted are: json, csv, html and text.
74-
func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
76+
func CreateReport(w io.Writer, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
7577
data := &reportInfo{
7678
Errors: errors,
7779
Issues: issues,
@@ -91,12 +93,50 @@ func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *go
9193
err = reportFromHTMLTemplate(w, html, data)
9294
case "text":
9395
err = reportFromPlaintextTemplate(w, text, data)
96+
case "sonarqube":
97+
err = reportSonarqube(rootPath, w, data)
9498
default:
9599
err = reportFromPlaintextTemplate(w, text, data)
96100
}
97101
return err
98102
}
99103

104+
func reportSonarqube(rootPath string, w io.Writer, data *reportInfo) error {
105+
var sonarIssues []sonarIssue
106+
for _, issue := range data.Issues {
107+
lines := strings.Split(issue.Line, "-")
108+
109+
startLine, _ := strconv.Atoi(lines[0])
110+
endLine := startLine
111+
if len(lines) > 1 {
112+
endLine, _ = strconv.Atoi(lines[1])
113+
}
114+
s := sonarIssue{
115+
EngineId: "gosec",
116+
RuleId: issue.RuleID,
117+
PrimaryLocation: location{
118+
Message: issue.What,
119+
FilePath: strings.Replace(issue.File, rootPath+"/", "", 1),
120+
TextRange: textRange{StartLine: startLine, EndLine: endLine},
121+
},
122+
Type: "VULNERABILITY",
123+
Severity: getSonarSeverity(issue.Severity.String()),
124+
EffortMinutes: 5,
125+
}
126+
sonarIssues = append(sonarIssues, s)
127+
}
128+
raw, err := json.MarshalIndent(sonarIssues, "", "\t")
129+
if err != nil {
130+
panic(err)
131+
}
132+
133+
_, err = w.Write(raw)
134+
if err != nil {
135+
panic(err)
136+
}
137+
return err
138+
}
139+
100140
func reportJSON(w io.Writer, data *reportInfo) error {
101141
raw, err := json.MarshalIndent(data, "", "\t")
102142
if err != nil {

output/sonarqube_format.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package output
2+
3+
type textRange struct {
4+
StartLine int `json:"startLine"`
5+
EndLine int `json:"endLine"`
6+
StartColumn int `json:"startColumn,omitempty"`
7+
EtartColumn int `json:"endColumn,omitempty"`
8+
}
9+
type location struct {
10+
Message string `json:"message"`
11+
FilePath string `json:"filePath"`
12+
TextRange textRange `json:"textRange,omitempty"`
13+
}
14+
15+
type sonarIssue struct {
16+
EngineId string `json:"engineId"`
17+
RuleId string `json:"ruleId"`
18+
PrimaryLocation location `json:"primaryLocation"`
19+
Type string `json:"type"`
20+
Severity string `json:"severity"`
21+
EffortMinutes int `json:"effortMinutes"`
22+
SecondaryLocations []location `json:"secondaryLocations,omitempty"`
23+
}
24+
25+
func getSonarSeverity(s string) string {
26+
switch s {
27+
case "LOW":
28+
return "MINOR"
29+
case "MEDIUM":
30+
return "MAJOR"
31+
case "HIGH":
32+
return "BLOCKER"
33+
default:
34+
return "INFO"
35+
}
36+
}

0 commit comments

Comments
 (0)