2020-11-02 08:13:53 +00:00
|
|
|
package output
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2021-02-05 09:06:04 +00:00
|
|
|
|
|
|
|
"github.com/securego/gosec/v2"
|
2020-11-02 08:13:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type sarifLevel string
|
|
|
|
|
|
|
|
const (
|
|
|
|
sarifNone = sarifLevel("none")
|
|
|
|
sarifNote = sarifLevel("note")
|
|
|
|
sarifWarning = sarifLevel("warning")
|
|
|
|
sarifError = sarifLevel("error")
|
|
|
|
)
|
|
|
|
|
|
|
|
type sarifProperties struct {
|
|
|
|
Tags []string `json:"tags"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifRule struct {
|
2021-01-22 09:15:52 +00:00
|
|
|
ID string `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
ShortDescription *sarifMessage `json:"shortDescription"`
|
|
|
|
FullDescription *sarifMessage `json:"fullDescription"`
|
|
|
|
Help *sarifMessage `json:"help"`
|
|
|
|
Properties *sarifProperties `json:"properties"`
|
|
|
|
DefaultConfiguration *sarifConfiguration `json:"defaultConfiguration"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifConfiguration struct {
|
|
|
|
Level sarifLevel `json:"level"`
|
2020-11-02 08:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type sarifArtifactLocation struct {
|
|
|
|
URI string `json:"uri"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifRegion struct {
|
|
|
|
StartLine uint64 `json:"startLine"`
|
2021-01-05 00:38:41 +00:00
|
|
|
EndLine uint64 `json:"endLine"`
|
2020-11-02 08:13:53 +00:00
|
|
|
StartColumn uint64 `json:"startColumn"`
|
|
|
|
EndColumn uint64 `json:"endColumn"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifPhysicalLocation struct {
|
|
|
|
ArtifactLocation *sarifArtifactLocation `json:"artifactLocation"`
|
|
|
|
Region *sarifRegion `json:"region"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifLocation struct {
|
|
|
|
PhysicalLocation *sarifPhysicalLocation `json:"physicalLocation"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifMessage struct {
|
|
|
|
Text string `json:"text"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifResult struct {
|
|
|
|
RuleID string `json:"ruleId"`
|
|
|
|
RuleIndex int `json:"ruleIndex"`
|
|
|
|
Level sarifLevel `json:"level"`
|
|
|
|
Message *sarifMessage `json:"message"`
|
|
|
|
Locations []*sarifLocation `json:"locations"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifDriver struct {
|
|
|
|
Name string `json:"name"`
|
2021-02-05 09:06:04 +00:00
|
|
|
Version string `json:"version"`
|
2020-11-02 08:13:53 +00:00
|
|
|
InformationURI string `json:"informationUri"`
|
|
|
|
Rules []*sarifRule `json:"rules,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifTool struct {
|
|
|
|
Driver *sarifDriver `json:"driver"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifRun struct {
|
|
|
|
Tool *sarifTool `json:"tool"`
|
|
|
|
Results []*sarifResult `json:"results"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sarifReport struct {
|
|
|
|
Schema string `json:"$schema"`
|
|
|
|
Version string `json:"version"`
|
|
|
|
Runs []*sarifRun `json:"runs"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildSarifReport return SARIF report struct
|
|
|
|
func buildSarifReport() *sarifReport {
|
|
|
|
return &sarifReport{
|
|
|
|
Version: "2.1.0",
|
2021-02-05 09:06:04 +00:00
|
|
|
Schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
2020-11-02 08:13:53 +00:00
|
|
|
Runs: []*sarifRun{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildSarifRule return SARIF rule field struct
|
|
|
|
func buildSarifRule(issue *gosec.Issue) *sarifRule {
|
|
|
|
return &sarifRule{
|
|
|
|
ID: fmt.Sprintf("%s (CWE-%s)", issue.RuleID, issue.Cwe.ID),
|
|
|
|
Name: issue.What,
|
|
|
|
ShortDescription: &sarifMessage{
|
|
|
|
Text: issue.What,
|
|
|
|
},
|
|
|
|
FullDescription: &sarifMessage{
|
|
|
|
Text: issue.What,
|
|
|
|
},
|
|
|
|
Help: &sarifMessage{
|
|
|
|
Text: fmt.Sprintf("%s\nSeverity: %s\nConfidence: %s\nCWE: %s", issue.What, issue.Severity.String(), issue.Confidence.String(), issue.Cwe.URL),
|
|
|
|
},
|
|
|
|
Properties: &sarifProperties{
|
|
|
|
Tags: []string{fmt.Sprintf("CWE-%s", issue.Cwe.ID), issue.Severity.String()},
|
|
|
|
},
|
2021-01-22 09:15:52 +00:00
|
|
|
DefaultConfiguration: &sarifConfiguration{
|
|
|
|
Level: getSarifLevel(issue.Severity.String()),
|
|
|
|
},
|
2020-11-02 08:13:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildSarifLocation return SARIF location struct
|
|
|
|
func buildSarifLocation(issue *gosec.Issue, rootPaths []string) (*sarifLocation, error) {
|
|
|
|
var filePath string
|
|
|
|
|
2021-01-05 00:38:41 +00:00
|
|
|
lines := strings.Split(issue.Line, "-")
|
|
|
|
startLine, err := strconv.ParseUint(lines[0], 10, 64)
|
2020-11-02 08:13:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-05 00:38:41 +00:00
|
|
|
endLine := startLine
|
|
|
|
if len(lines) > 1 {
|
|
|
|
endLine, err = strconv.ParseUint(lines[1], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 08:13:53 +00:00
|
|
|
col, err := strconv.ParseUint(issue.Col, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rootPath := range rootPaths {
|
|
|
|
if strings.HasPrefix(issue.File, rootPath) {
|
|
|
|
filePath = strings.Replace(issue.File, rootPath+"/", "", 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
location := &sarifLocation{
|
|
|
|
PhysicalLocation: &sarifPhysicalLocation{
|
|
|
|
ArtifactLocation: &sarifArtifactLocation{
|
|
|
|
URI: filePath,
|
|
|
|
},
|
|
|
|
Region: &sarifRegion{
|
2021-01-05 00:38:41 +00:00
|
|
|
StartLine: startLine,
|
|
|
|
EndLine: endLine,
|
2020-11-02 08:13:53 +00:00
|
|
|
StartColumn: col,
|
|
|
|
EndColumn: col,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return location, nil
|
|
|
|
}
|
2021-01-21 16:51:27 +00:00
|
|
|
|
|
|
|
// From https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html#_Toc10127839
|
|
|
|
// * "warning": The rule specified by ruleId was evaluated and a problem was found.
|
|
|
|
// * "error": The rule specified by ruleId was evaluated and a serious problem was found.
|
|
|
|
// * "note": The rule specified by ruleId was evaluated and a minor problem or an opportunity to improve the code was found.
|
|
|
|
func getSarifLevel(s string) sarifLevel {
|
|
|
|
switch s {
|
|
|
|
case "LOW":
|
|
|
|
return sarifWarning
|
|
|
|
case "MEDIUM":
|
|
|
|
return sarifError
|
|
|
|
case "HIGH":
|
|
|
|
return sarifError
|
|
|
|
default:
|
|
|
|
return sarifNote
|
|
|
|
}
|
|
|
|
}
|