Skip to content

Commit 4f5251d

Browse files
SophisticaSeanldez
andauthored
Support Sarif output (#4723)
Co-authored-by: Fernandez Ludovic <[email protected]>
1 parent 73110df commit 4f5251d

File tree

6 files changed

+183
-1
lines changed

6 files changed

+183
-1
lines changed

.golangci.next.reference.yml

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ output:
7171
# - `junit-xml`
7272
# - `github-actions`
7373
# - `teamcity`
74+
# - `sarif`
7475
# Output path can be either `stdout`, `stderr` or path to the file to write to.
7576
#
7677
# For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.

jsonschema/golangci.next.jsonschema.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@
432432
"code-climate",
433433
"junit-xml",
434434
"github-actions",
435-
"teamcity"
435+
"teamcity",
436+
"sarif"
436437
]
437438
}
438439
},

pkg/config/output.go

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const (
1919
OutFormatJunitXML = "junit-xml"
2020
OutFormatGithubActions = "github-actions"
2121
OutFormatTeamCity = "teamcity"
22+
OutFormatSarif = "sarif"
2223
)
2324

2425
var AllOutputFormats = []string{
@@ -33,6 +34,7 @@ var AllOutputFormats = []string{
3334
OutFormatJunitXML,
3435
OutFormatGithubActions,
3536
OutFormatTeamCity,
37+
OutFormatSarif,
3638
}
3739

3840
type Output struct {

pkg/printers/printer.go

+2
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ func (c *Printer) createPrinter(format string, w io.Writer) (issuePrinter, error
135135
p = NewGitHubAction(w)
136136
case config.OutFormatTeamCity:
137137
p = NewTeamCity(w)
138+
case config.OutFormatSarif:
139+
p = NewSarif(w)
138140
default:
139141
return nil, fmt.Errorf("unknown output format %q", format)
140142
}

pkg/printers/sarif.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package printers
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
7+
"github.com/golangci/golangci-lint/pkg/result"
8+
)
9+
10+
const (
11+
sarifVersion = "2.1.0"
12+
sarifSchemaURI = "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.6.json"
13+
)
14+
15+
type SarifOutput struct {
16+
Version string `json:"version"`
17+
Schema string `json:"$schema"`
18+
Runs []sarifRun `json:"runs"`
19+
}
20+
21+
type sarifRun struct {
22+
Tool sarifTool `json:"tool"`
23+
Results []sarifResult `json:"results"`
24+
}
25+
26+
type sarifTool struct {
27+
Driver struct {
28+
Name string `json:"name"`
29+
} `json:"driver"`
30+
}
31+
32+
type sarifResult struct {
33+
RuleID string `json:"ruleId"`
34+
Level string `json:"level"`
35+
Message sarifMessage `json:"message"`
36+
Locations []sarifLocation `json:"locations"`
37+
}
38+
39+
type sarifMessage struct {
40+
Text string `json:"text"`
41+
}
42+
43+
type sarifLocation struct {
44+
PhysicalLocation sarifPhysicalLocation `json:"physicalLocation"`
45+
}
46+
47+
type sarifPhysicalLocation struct {
48+
ArtifactLocation sarifArtifactLocation `json:"artifactLocation"`
49+
Region sarifRegion `json:"region"`
50+
}
51+
52+
type sarifArtifactLocation struct {
53+
URI string `json:"uri"`
54+
Index int `json:"index"`
55+
}
56+
57+
type sarifRegion struct {
58+
StartLine int `json:"startLine"`
59+
StartColumn int `json:"startColumn"`
60+
}
61+
62+
type Sarif struct {
63+
w io.Writer
64+
}
65+
66+
func NewSarif(w io.Writer) *Sarif {
67+
return &Sarif{w: w}
68+
}
69+
70+
func (p Sarif) Print(issues []result.Issue) error {
71+
run := sarifRun{}
72+
run.Tool.Driver.Name = "golangci-lint"
73+
74+
for i := range issues {
75+
issue := issues[i]
76+
77+
severity := issue.Severity
78+
if severity == "" {
79+
severity = "error"
80+
}
81+
82+
sr := sarifResult{
83+
RuleID: issue.FromLinter,
84+
Level: severity,
85+
Message: sarifMessage{Text: issue.Text},
86+
Locations: []sarifLocation{
87+
{
88+
PhysicalLocation: sarifPhysicalLocation{
89+
ArtifactLocation: sarifArtifactLocation{URI: issue.FilePath()},
90+
Region: sarifRegion{
91+
StartLine: issue.Line(),
92+
StartColumn: issue.Column(),
93+
},
94+
},
95+
},
96+
},
97+
}
98+
99+
run.Results = append(run.Results, sr)
100+
}
101+
102+
output := SarifOutput{
103+
Version: sarifVersion,
104+
Schema: sarifSchemaURI,
105+
Runs: []sarifRun{run},
106+
}
107+
108+
return json.NewEncoder(p.w).Encode(output)
109+
}

pkg/printers/sarif_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package printers
2+
3+
import (
4+
"bytes"
5+
"go/token"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/golangci/golangci-lint/pkg/result"
12+
)
13+
14+
func TestSarif_Print(t *testing.T) {
15+
issues := []result.Issue{
16+
{
17+
FromLinter: "linter-a",
18+
Severity: "warning",
19+
Text: "some issue",
20+
Pos: token.Position{
21+
Filename: "path/to/filea.go",
22+
Offset: 2,
23+
Line: 10,
24+
Column: 4,
25+
},
26+
},
27+
{
28+
FromLinter: "linter-b",
29+
Severity: "error",
30+
Text: "another issue",
31+
SourceLines: []string{
32+
"func foo() {",
33+
"\tfmt.Println(\"bar\")",
34+
"}",
35+
},
36+
Pos: token.Position{
37+
Filename: "path/to/fileb.go",
38+
Offset: 5,
39+
Line: 300,
40+
Column: 9,
41+
},
42+
},
43+
{
44+
FromLinter: "linter-a",
45+
Severity: "error",
46+
Text: "some issue 2",
47+
Pos: token.Position{
48+
Filename: "path/to/filec.go",
49+
Offset: 3,
50+
Line: 11,
51+
Column: 5,
52+
},
53+
},
54+
}
55+
56+
buf := new(bytes.Buffer)
57+
58+
printer := NewSarif(buf)
59+
60+
err := printer.Print(issues)
61+
require.NoError(t, err)
62+
63+
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}}}]}]}]}
64+
`
65+
66+
assert.Equal(t, expected, buf.String())
67+
}

0 commit comments

Comments
 (0)