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`
|
# - `junit-xml`
|
||||||
# - `github-actions`
|
# - `github-actions`
|
||||||
# - `teamcity`
|
# - `teamcity`
|
||||||
|
# - `sarif`
|
||||||
# Output path can be either `stdout`, `stderr` or path to the file to write to.
|
# 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.
|
# For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.
|
||||||
|
@ -432,7 +432,8 @@
|
|||||||
"code-climate",
|
"code-climate",
|
||||||
"junit-xml",
|
"junit-xml",
|
||||||
"github-actions",
|
"github-actions",
|
||||||
"teamcity"
|
"teamcity",
|
||||||
|
"sarif"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
OutFormatJunitXML = "junit-xml"
|
OutFormatJunitXML = "junit-xml"
|
||||||
OutFormatGithubActions = "github-actions"
|
OutFormatGithubActions = "github-actions"
|
||||||
OutFormatTeamCity = "teamcity"
|
OutFormatTeamCity = "teamcity"
|
||||||
|
OutFormatSarif = "sarif"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AllOutputFormats = []string{
|
var AllOutputFormats = []string{
|
||||||
@ -33,6 +34,7 @@ var AllOutputFormats = []string{
|
|||||||
OutFormatJunitXML,
|
OutFormatJunitXML,
|
||||||
OutFormatGithubActions,
|
OutFormatGithubActions,
|
||||||
OutFormatTeamCity,
|
OutFormatTeamCity,
|
||||||
|
OutFormatSarif,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Output struct {
|
type Output struct {
|
||||||
|
@ -135,6 +135,8 @@ func (c *Printer) createPrinter(format string, w io.Writer) (issuePrinter, error
|
|||||||
p = NewGitHubAction(w)
|
p = NewGitHubAction(w)
|
||||||
case config.OutFormatTeamCity:
|
case config.OutFormatTeamCity:
|
||||||
p = NewTeamCity(w)
|
p = NewTeamCity(w)
|
||||||
|
case config.OutFormatSarif:
|
||||||
|
p = NewSarif(w)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown output format %q", format)
|
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