mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 03:55:54 +00:00
Add CWE rule mappings (#405)
* added mappings * added cwe to template * link in function to template * moved mappings and added test cases * wording * cleanup
This commit is contained in:
parent
28c1128b73
commit
53be8dd864
6 changed files with 235 additions and 4 deletions
41
issue.go
41
issue.go
|
@ -34,10 +34,50 @@ const (
|
||||||
High
|
High
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Cwe id and url
|
||||||
|
type Cwe struct {
|
||||||
|
ID string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCwe creates a cwe object for a given RuleID
|
||||||
|
func GetCwe(id string) Cwe {
|
||||||
|
return Cwe{ID: id, URL: fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", id)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueToCWE maps gosec rules to CWEs
|
||||||
|
var IssueToCWE = map[string]Cwe{
|
||||||
|
"G101": GetCwe("798"),
|
||||||
|
"G102": GetCwe("200"),
|
||||||
|
"G103": GetCwe("242"),
|
||||||
|
"G104": GetCwe("703"),
|
||||||
|
"G106": GetCwe("322"),
|
||||||
|
"G107": GetCwe("88"),
|
||||||
|
"G201": GetCwe("89"),
|
||||||
|
"G202": GetCwe("89"),
|
||||||
|
"G203": GetCwe("79"),
|
||||||
|
"G204": GetCwe("78"),
|
||||||
|
"G301": GetCwe("276"),
|
||||||
|
"G302": GetCwe("276"),
|
||||||
|
"G303": GetCwe("377"),
|
||||||
|
"G304": GetCwe("22"),
|
||||||
|
"G305": GetCwe("22"),
|
||||||
|
"G401": GetCwe("326"),
|
||||||
|
"G402": GetCwe("295"),
|
||||||
|
"G403": GetCwe("310"),
|
||||||
|
"G404": GetCwe("338"),
|
||||||
|
"G501": GetCwe("327"),
|
||||||
|
"G502": GetCwe("327"),
|
||||||
|
"G503": GetCwe("327"),
|
||||||
|
"G504": GetCwe("327"),
|
||||||
|
"G505": GetCwe("327"),
|
||||||
|
}
|
||||||
|
|
||||||
// Issue is returned by a gosec rule if it discovers an issue with the scanned code.
|
// Issue is returned by a gosec rule if it discovers an issue with the scanned code.
|
||||||
type Issue struct {
|
type Issue struct {
|
||||||
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
||||||
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
||||||
|
Cwe Cwe `json:"cwe"` // Cwe associated with RuleID
|
||||||
RuleID string `json:"rule_id"` // Human readable explanation
|
RuleID string `json:"rule_id"` // Human readable explanation
|
||||||
What string `json:"details"` // Human readable explanation
|
What string `json:"details"` // Human readable explanation
|
||||||
File string `json:"file"` // File name we found it in
|
File string `json:"file"` // File name we found it in
|
||||||
|
@ -121,5 +161,6 @@ func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score,
|
||||||
Confidence: confidence,
|
Confidence: confidence,
|
||||||
Severity: severity,
|
Severity: severity,
|
||||||
Code: code,
|
Code: code,
|
||||||
|
Cwe: IssueToCWE[ruleID],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ var _ = Describe("Issue", func() {
|
||||||
Expect(issue).ShouldNot(BeNil())
|
Expect(issue).ShouldNot(BeNil())
|
||||||
Expect(issue.Code).Should(MatchRegexp(`"bar"`))
|
Expect(issue.Code).Should(MatchRegexp(`"bar"`))
|
||||||
Expect(issue.Line).Should(Equal("2"))
|
Expect(issue.Line).Should(Equal("2"))
|
||||||
|
Expect(issue.Cwe.ID).Should(Equal(""))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should return an error if specific context is not able to be obtained", func() {
|
It("should return an error if specific context is not able to be obtained", func() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
htmlTemplate "html/template"
|
htmlTemplate "html/template"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -56,7 +57,7 @@ Golang errors in file: [{{ $filePath }}]:
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ range $index, $issue := .Issues }}
|
{{ range $index, $issue := .Issues }}
|
||||||
[{{ $issue.File }}:{{ $issue.Line }}] - {{ $issue.RuleID }}: {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
|
[{{ $issue.File }}:{{ $issue.Line }}] - {{ $issue.RuleID }} (CWE-{{ $issue.Cwe.ID }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
|
||||||
> {{ $issue.Code }}
|
> {{ $issue.Code }}
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -126,6 +127,7 @@ func convertToSonarIssues(rootPaths []string, data *reportInfo) (*sonarIssues, e
|
||||||
sonarFilePath = strings.Replace(issue.File, rootPath+"/", "", 1)
|
sonarFilePath = strings.Replace(issue.File, rootPath+"/", "", 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sonarFilePath == "" {
|
if sonarFilePath == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -154,6 +156,7 @@ func convertToSonarIssues(rootPaths []string, data *reportInfo) (*sonarIssues, e
|
||||||
Type: "VULNERABILITY",
|
Type: "VULNERABILITY",
|
||||||
Severity: getSonarSeverity(issue.Severity.String()),
|
Severity: getSonarSeverity(issue.Severity.String()),
|
||||||
EffortMinutes: SonarqubeEffortMinutes,
|
EffortMinutes: SonarqubeEffortMinutes,
|
||||||
|
Cwe: issue.Cwe,
|
||||||
}
|
}
|
||||||
si.SonarIssues = append(si.SonarIssues, s)
|
si.SonarIssues = append(si.SonarIssues, s)
|
||||||
}
|
}
|
||||||
|
@ -190,6 +193,7 @@ func reportCSV(w io.Writer, data *reportInfo) error {
|
||||||
issue.Severity.String(),
|
issue.Severity.String(),
|
||||||
issue.Confidence.String(),
|
issue.Confidence.String(),
|
||||||
issue.Code,
|
issue.Code,
|
||||||
|
fmt.Sprintf("CWE-%s", issue.Cwe.ID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,12 +1,48 @@
|
||||||
package output
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/securego/gosec"
|
"github.com/securego/gosec"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func createIssue(ruleID string, cwe gosec.Cwe) gosec.Issue {
|
||||||
|
return gosec.Issue{
|
||||||
|
File: "/home/src/project/test.go",
|
||||||
|
Line: "1",
|
||||||
|
RuleID: ruleID,
|
||||||
|
What: "test",
|
||||||
|
Confidence: gosec.High,
|
||||||
|
Severity: gosec.High,
|
||||||
|
Code: "testcode",
|
||||||
|
Cwe: cwe,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createReportInfo(rule string, cwe gosec.Cwe) reportInfo {
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
metrics := gosec.Metrics{}
|
||||||
|
return reportInfo{
|
||||||
|
Errors: map[string][]gosec.Error{},
|
||||||
|
Issues: []*gosec.Issue{
|
||||||
|
&issue,
|
||||||
|
},
|
||||||
|
Stats: &metrics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripString(str string) string {
|
||||||
|
ret := strings.Replace(str, "\n", "", -1)
|
||||||
|
ret = strings.Replace(ret, " ", "", -1)
|
||||||
|
ret = strings.Replace(ret, "\t", "", -1)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
var _ = Describe("Formatter", func() {
|
var _ = Describe("Formatter", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
})
|
})
|
||||||
|
@ -210,4 +246,150 @@ var _ = Describe("Formatter", func() {
|
||||||
Expect(*issues).To(Equal(*want))
|
Expect(*issues).To(Equal(*want))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Context("When using different report formats", func() {
|
||||||
|
|
||||||
|
grules := []string{"G101", "G102", "G103", "G104", "G106",
|
||||||
|
"G107", "G201", "G202", "G203", "G204", "G301",
|
||||||
|
"G302", "G303", "G304", "G305", "G401", "G402",
|
||||||
|
"G403", "G404", "G501", "G502", "G503", "G504", "G505"}
|
||||||
|
|
||||||
|
It("csv formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "csv", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
pattern := "/home/src/project/test.go,1,test,HIGH,HIGH,testcode,CWE-%s\n"
|
||||||
|
expect := fmt.Sprintf(pattern, cwe.ID)
|
||||||
|
Expect(string(buf.Bytes())).To(Equal(expect))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("xml formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "xml", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{NumFiles: 0, NumLines: 0, NumNosec: 0, NumFound: 0}, error)
|
||||||
|
pattern := "Results:\n\n\n[/home/src/project/test.go:1] - %s (CWE-%s): test (Confidence: HIGH, Severity: HIGH)\n > testcode\n\n\nSummary:\n Files: 0\n Lines: 0\n Nosec: 0\n Issues: 0\n\n"
|
||||||
|
expect := fmt.Sprintf(pattern, rule, cwe.ID)
|
||||||
|
Expect(string(buf.Bytes())).To(Equal(expect))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("json formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
data := createReportInfo(rule, cwe)
|
||||||
|
|
||||||
|
expect := new(bytes.Buffer)
|
||||||
|
enc := json.NewEncoder(expect)
|
||||||
|
enc.Encode(data)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "json", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
result := stripString(buf.String())
|
||||||
|
expectation := stripString(expect.String())
|
||||||
|
Expect(result).To(Equal(expectation))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("html formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
data := createReportInfo(rule, cwe)
|
||||||
|
|
||||||
|
expect := new(bytes.Buffer)
|
||||||
|
enc := json.NewEncoder(expect)
|
||||||
|
enc.Encode(data)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "html", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
result := stripString(buf.String())
|
||||||
|
expectation := stripString(expect.String())
|
||||||
|
Expect(result).To(ContainSubstring(expectation))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("yaml formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
data := createReportInfo(rule, cwe)
|
||||||
|
|
||||||
|
expect := new(bytes.Buffer)
|
||||||
|
enc := yaml.NewEncoder(expect)
|
||||||
|
enc.Encode(data)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "yaml", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
result := stripString(buf.String())
|
||||||
|
expectation := stripString(expect.String())
|
||||||
|
Expect(result).To(ContainSubstring(expectation))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("junit-xml formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
data := createReportInfo(rule, cwe)
|
||||||
|
|
||||||
|
expect := new(bytes.Buffer)
|
||||||
|
enc := yaml.NewEncoder(expect)
|
||||||
|
enc.Encode(data)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "junit-xml", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
expectation := stripString(fmt.Sprintf("[/home/src/project/test.go:1] - test (Confidence: 2, Severity: 2, CWE: %s)", cwe.ID))
|
||||||
|
result := stripString(buf.String())
|
||||||
|
Expect(result).To(ContainSubstring(expectation))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("text formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
|
||||||
|
data := createReportInfo(rule, cwe)
|
||||||
|
|
||||||
|
expect := new(bytes.Buffer)
|
||||||
|
enc := yaml.NewEncoder(expect)
|
||||||
|
enc.Encode(data)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "text", []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
expectation := stripString(fmt.Sprintf("[/home/src/project/test.go:1] - %s (CWE-%s): test (Confidence: HIGH, Severity: HIGH)", rule, cwe.ID))
|
||||||
|
result := stripString(buf.String())
|
||||||
|
Expect(result).To(ContainSubstring(expectation))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("sonarqube formatted report should contain the CWE mapping", func() {
|
||||||
|
for _, rule := range grules {
|
||||||
|
cwe := gosec.IssueToCWE[rule]
|
||||||
|
issue := createIssue(rule, cwe)
|
||||||
|
error := map[string][]gosec.Error{}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
CreateReport(buf, "sonarqube", []string{"/home/src/project"}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
|
||||||
|
result := stripString(buf.String())
|
||||||
|
|
||||||
|
expect := new(bytes.Buffer)
|
||||||
|
enc := json.NewEncoder(expect)
|
||||||
|
enc.Encode(cwe)
|
||||||
|
|
||||||
|
expectation := stripString(expect.String())
|
||||||
|
Expect(result).To(ContainSubstring(expectation))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -36,7 +36,8 @@ func generatePlaintext(issue *gosec.Issue) string {
|
||||||
return "Results:\n" +
|
return "Results:\n" +
|
||||||
"[" + issue.File + ":" + issue.Line + "] - " +
|
"[" + issue.File + ":" + issue.Line + "] - " +
|
||||||
issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) +
|
issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) +
|
||||||
", Severity: " + strconv.Itoa(int(issue.Severity)) + ")\n" + "> " + htmlLib.EscapeString(issue.Code)
|
", Severity: " + strconv.Itoa(int(issue.Severity)) +
|
||||||
|
", CWE: " + issue.Cwe.ID + ")\n" + "> " + htmlLib.EscapeString(issue.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupDataByRules(data *reportInfo) map[string][]*gosec.Issue {
|
func groupDataByRules(data *reportInfo) map[string][]*gosec.Issue {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package output
|
package output
|
||||||
|
|
||||||
|
import "github.com/securego/gosec"
|
||||||
|
|
||||||
type textRange struct {
|
type textRange struct {
|
||||||
StartLine int `json:"startLine"`
|
StartLine int `json:"startLine"`
|
||||||
EndLine int `json:"endLine"`
|
EndLine int `json:"endLine"`
|
||||||
|
@ -15,6 +17,7 @@ type location struct {
|
||||||
type sonarIssue struct {
|
type sonarIssue struct {
|
||||||
EngineID string `json:"engineId"`
|
EngineID string `json:"engineId"`
|
||||||
RuleID string `json:"ruleId"`
|
RuleID string `json:"ruleId"`
|
||||||
|
Cwe gosec.Cwe `json:"cwe"`
|
||||||
PrimaryLocation location `json:"primaryLocation"`
|
PrimaryLocation location `json:"primaryLocation"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Severity string `json:"severity"`
|
Severity string `json:"severity"`
|
||||||
|
|
Loading…
Reference in a new issue