1
0
mirror of https://github.com/securego/gosec.git synced 2025-11-27 22:28:20 +02:00

Add sonarqube output

This commit is contained in:
kencrawford
2019-03-11 16:13:48 -04:00
committed by Grant Murphy
parent c5e6c4aedd
commit ddfe54d0a0
3 changed files with 84 additions and 7 deletions

View File

@@ -65,7 +65,7 @@ var (
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set") flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
// format output // format output
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, or text") flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, or text")
// output file // output file
flagOutput = flag.String("out", "", "Set output file for results") flagOutput = flag.String("out", "", "Set output file for results")
@@ -165,19 +165,19 @@ func loadRules(include, exclude string) rules.RuleList {
return rules.Generate(filters...) return rules.Generate(filters...)
} }
func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error { func saveOutput(filename, format, rootPath string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
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, issues, metrics, errors) err = output.CreateReport(outfile, format, rootPath, issues, metrics, errors)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
err := output.CreateReport(os.Stdout, format, issues, metrics, errors) err := output.CreateReport(os.Stdout, format, rootPath, issues, metrics, errors)
if err != nil { if err != nil {
return err return err
} }
@@ -318,6 +318,7 @@ func main() {
if *flagBuildTags != "" { if *flagBuildTags != "" {
buildTags = strings.Split(*flagBuildTags, ",") buildTags = strings.Split(*flagBuildTags, ",")
} }
if err := analyzer.Process(buildTags, packages...); err != nil { if err := analyzer.Process(buildTags, packages...); err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
@@ -342,9 +343,9 @@ func main() {
if !issuesFound && *flagQuiet { if !issuesFound && *flagQuiet {
os.Exit(0) os.Exit(0)
} }
rootPath := packages[0]
// Create output report // Create output report
if err := saveOutput(*flagOutput, *flagFormat, issues, metrics, errors); err != nil { if err := saveOutput(*flagOutput, *flagFormat, rootPath, issues, metrics, errors); err != nil {
logger.Fatal(err) logger.Fatal(err)
} }

View File

@@ -20,6 +20,8 @@ import (
"encoding/xml" "encoding/xml"
htmlTemplate "html/template" htmlTemplate "html/template"
"io" "io"
"strconv"
"strings"
plainTemplate "text/template" plainTemplate "text/template"
"github.com/securego/gosec" "github.com/securego/gosec"
@@ -71,7 +73,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 string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error { func CreateReport(w io.Writer, format, rootPath 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,
@@ -91,12 +93,50 @@ func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *go
err = reportFromHTMLTemplate(w, html, data) err = reportFromHTMLTemplate(w, html, data)
case "text": case "text":
err = reportFromPlaintextTemplate(w, text, data) err = reportFromPlaintextTemplate(w, text, data)
case "sonarqube":
err = reportSonarqube(rootPath, 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 {
var sonarIssues []sonarIssue
for _, issue := range data.Issues {
lines := strings.Split(issue.Line, "-")
startLine, _ := strconv.Atoi(lines[0])
endLine := startLine
if len(lines) > 1 {
endLine, _ = strconv.Atoi(lines[1])
}
s := sonarIssue{
EngineId: "gosec",
RuleId: issue.RuleID,
PrimaryLocation: location{
Message: issue.What,
FilePath: strings.Replace(issue.File, rootPath+"/", "", 1),
TextRange: textRange{StartLine: startLine, EndLine: endLine},
},
Type: "VULNERABILITY",
Severity: getSonarSeverity(issue.Severity.String()),
EffortMinutes: 5,
}
sonarIssues = append(sonarIssues, s)
}
raw, err := json.MarshalIndent(sonarIssues, "", "\t")
if err != nil {
panic(err)
}
_, err = w.Write(raw)
if err != nil {
panic(err)
}
return err
}
func reportJSON(w io.Writer, data *reportInfo) error { func reportJSON(w io.Writer, data *reportInfo) error {
raw, err := json.MarshalIndent(data, "", "\t") raw, err := json.MarshalIndent(data, "", "\t")
if err != nil { if err != nil {

View File

@@ -0,0 +1,36 @@
package output
type textRange struct {
StartLine int `json:"startLine"`
EndLine int `json:"endLine"`
StartColumn int `json:"startColumn,omitempty"`
EtartColumn int `json:"endColumn,omitempty"`
}
type location struct {
Message string `json:"message"`
FilePath string `json:"filePath"`
TextRange textRange `json:"textRange,omitempty"`
}
type sonarIssue struct {
EngineId string `json:"engineId"`
RuleId string `json:"ruleId"`
PrimaryLocation location `json:"primaryLocation"`
Type string `json:"type"`
Severity string `json:"severity"`
EffortMinutes int `json:"effortMinutes"`
SecondaryLocations []location `json:"secondaryLocations,omitempty"`
}
func getSonarSeverity(s string) string {
switch s {
case "LOW":
return "MINOR"
case "MEDIUM":
return "MAJOR"
case "HIGH":
return "BLOCKER"
default:
return "INFO"
}
}