Support Sarif output (#4723)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
parent
73110df2ce
commit
4f5251d3c8
@ -71,6 +71,7 @@ output:
|
||||
# - `junit-xml`
|
||||
# - `github-actions`
|
||||
# - `teamcity`
|
||||
# - `sarif`
|
||||
# Output path can be either `stdout`, `stderr` or path to the file to write to.
|
||||
#
|
||||
# For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.
|
||||
|
@ -432,7 +432,8 @@
|
||||
"code-climate",
|
||||
"junit-xml",
|
||||
"github-actions",
|
||||
"teamcity"
|
||||
"teamcity",
|
||||
"sarif"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -19,6 +19,7 @@ const (
|
||||
OutFormatJunitXML = "junit-xml"
|
||||
OutFormatGithubActions = "github-actions"
|
||||
OutFormatTeamCity = "teamcity"
|
||||
OutFormatSarif = "sarif"
|
||||
)
|
||||
|
||||
var AllOutputFormats = []string{
|
||||
@ -33,6 +34,7 @@ var AllOutputFormats = []string{
|
||||
OutFormatJunitXML,
|
||||
OutFormatGithubActions,
|
||||
OutFormatTeamCity,
|
||||
OutFormatSarif,
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
|
@ -135,6 +135,8 @@ func (c *Printer) createPrinter(format string, w io.Writer) (issuePrinter, error
|
||||
p = NewGitHubAction(w)
|
||||
case config.OutFormatTeamCity:
|
||||
p = NewTeamCity(w)
|
||||
case config.OutFormatSarif:
|
||||
p = NewSarif(w)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown output format %q", format)
|
||||
}
|
||||
|
109
pkg/printers/sarif.go
Normal file
109
pkg/printers/sarif.go
Normal file
@ -0,0 +1,109 @@
|
||||
package printers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const (
|
||||
sarifVersion = "2.1.0"
|
||||
sarifSchemaURI = "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.6.json"
|
||||
)
|
||||
|
||||
type SarifOutput struct {
|
||||
Version string `json:"version"`
|
||||
Schema string `json:"$schema"`
|
||||
Runs []sarifRun `json:"runs"`
|
||||
}
|
||||
|
||||
type sarifRun struct {
|
||||
Tool sarifTool `json:"tool"`
|
||||
Results []sarifResult `json:"results"`
|
||||
}
|
||||
|
||||
type sarifTool struct {
|
||||
Driver struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"driver"`
|
||||
}
|
||||
|
||||
type sarifResult struct {
|
||||
RuleID string `json:"ruleId"`
|
||||
Level string `json:"level"`
|
||||
Message sarifMessage `json:"message"`
|
||||
Locations []sarifLocation `json:"locations"`
|
||||
}
|
||||
|
||||
type sarifMessage struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type sarifLocation struct {
|
||||
PhysicalLocation sarifPhysicalLocation `json:"physicalLocation"`
|
||||
}
|
||||
|
||||
type sarifPhysicalLocation struct {
|
||||
ArtifactLocation sarifArtifactLocation `json:"artifactLocation"`
|
||||
Region sarifRegion `json:"region"`
|
||||
}
|
||||
|
||||
type sarifArtifactLocation struct {
|
||||
URI string `json:"uri"`
|
||||
Index int `json:"index"`
|
||||
}
|
||||
|
||||
type sarifRegion struct {
|
||||
StartLine int `json:"startLine"`
|
||||
StartColumn int `json:"startColumn"`
|
||||
}
|
||||
|
||||
type Sarif struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func NewSarif(w io.Writer) *Sarif {
|
||||
return &Sarif{w: w}
|
||||
}
|
||||
|
||||
func (p Sarif) Print(issues []result.Issue) error {
|
||||
run := sarifRun{}
|
||||
run.Tool.Driver.Name = "golangci-lint"
|
||||
|
||||
for i := range issues {
|
||||
issue := issues[i]
|
||||
|
||||
severity := issue.Severity
|
||||
if severity == "" {
|
||||
severity = "error"
|
||||
}
|
||||
|
||||
sr := sarifResult{
|
||||
RuleID: issue.FromLinter,
|
||||
Level: severity,
|
||||
Message: sarifMessage{Text: issue.Text},
|
||||
Locations: []sarifLocation{
|
||||
{
|
||||
PhysicalLocation: sarifPhysicalLocation{
|
||||
ArtifactLocation: sarifArtifactLocation{URI: issue.FilePath()},
|
||||
Region: sarifRegion{
|
||||
StartLine: issue.Line(),
|
||||
StartColumn: issue.Column(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
run.Results = append(run.Results, sr)
|
||||
}
|
||||
|
||||
output := SarifOutput{
|
||||
Version: sarifVersion,
|
||||
Schema: sarifSchemaURI,
|
||||
Runs: []sarifRun{run},
|
||||
}
|
||||
|
||||
return json.NewEncoder(p.w).Encode(output)
|
||||
}
|
67
pkg/printers/sarif_test.go
Normal file
67
pkg/printers/sarif_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package printers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
func TestSarif_Print(t *testing.T) {
|
||||
issues := []result.Issue{
|
||||
{
|
||||
FromLinter: "linter-a",
|
||||
Severity: "warning",
|
||||
Text: "some issue",
|
||||
Pos: token.Position{
|
||||
Filename: "path/to/filea.go",
|
||||
Offset: 2,
|
||||
Line: 10,
|
||||
Column: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
FromLinter: "linter-b",
|
||||
Severity: "error",
|
||||
Text: "another issue",
|
||||
SourceLines: []string{
|
||||
"func foo() {",
|
||||
"\tfmt.Println(\"bar\")",
|
||||
"}",
|
||||
},
|
||||
Pos: token.Position{
|
||||
Filename: "path/to/fileb.go",
|
||||
Offset: 5,
|
||||
Line: 300,
|
||||
Column: 9,
|
||||
},
|
||||
},
|
||||
{
|
||||
FromLinter: "linter-a",
|
||||
Severity: "error",
|
||||
Text: "some issue 2",
|
||||
Pos: token.Position{
|
||||
Filename: "path/to/filec.go",
|
||||
Offset: 3,
|
||||
Line: 11,
|
||||
Column: 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
printer := NewSarif(buf)
|
||||
|
||||
err := printer.Print(issues)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := `{"version":"2.1.0","$schema":"https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.6.json","runs":[{"tool":{"driver":{"name":"golangci-lint"}},"results":[{"ruleId":"linter-a","level":"warning","message":{"text":"some issue"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/filea.go","index":0},"region":{"startLine":10,"startColumn":4}}}]},{"ruleId":"linter-b","level":"error","message":{"text":"another issue"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/fileb.go","index":0},"region":{"startLine":300,"startColumn":9}}}]},{"ruleId":"linter-a","level":"error","message":{"text":"some issue 2"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/filec.go","index":0},"region":{"startLine":11,"startColumn":5}}}]}]}]}
|
||||
`
|
||||
|
||||
assert.Equal(t, expected, buf.String())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user