From cf03ef6779032af7246f56c4f60586de6ac4e28e Mon Sep 17 00:00:00 2001 From: Sean Lewis Date: Thu, 16 May 2024 02:32:54 -0600 Subject: [PATCH 1/7] tests working --- pkg/config/output.go | 2 + pkg/printers/sarif.go | 126 +++++++++++++++++++++++++++++++++++++ pkg/printers/sarif_test.go | 67 ++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 pkg/printers/sarif.go create mode 100644 pkg/printers/sarif_test.go diff --git a/pkg/config/output.go b/pkg/config/output.go index a005213cfdce..19d306949089 100644 --- a/pkg/config/output.go +++ b/pkg/config/output.go @@ -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 { diff --git a/pkg/printers/sarif.go b/pkg/printers/sarif.go new file mode 100644 index 000000000000..47fe7bd99a9a --- /dev/null +++ b/pkg/printers/sarif.go @@ -0,0 +1,126 @@ +package printers + +import ( + "encoding/json" + "io" + + "github.com/golangci/golangci-lint/pkg/report" + "github.com/golangci/golangci-lint/pkg/result" +) + +const ( + sarifVersion = "2.1.0" + sarifSchemaURI = "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json" +) + +type SarifResult 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 struct { + Text string `json:"text"` + } `json:"message"` + Locations []sarifLocation `json:"locations"` +} + +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 { + rd *report.Data + w io.Writer +} + +func NewSarif(rd *report.Data, w io.Writer) *Sarif { + return &Sarif{ + rd: rd, + w: w, + } +} + +// type SarifResult struct { +// Issues []result.Issue +// Report *report.Data +// } + +func (p Sarif) Print(issues []result.Issue) error { + res := SarifResult{} + res.Version = sarifVersion + res.Schema = sarifSchemaURI + + toolMap := map[string][]result.Issue{} + + for i := range issues { + issue := issues[i] + linter := issue.FromLinter + toolMap[linter] = append(toolMap[linter], issue) + } + + for curtool, issues := range toolMap { + tool := sarifTool{} + tool.Driver.Name = curtool + sr := sarifRun{} + sr.Tool = tool + + for i := range issues { + issue := issues[i] + physLoc := sarifPhysicalLocation{ + ArtifactLocation: sarifArtifactLocation{URI: issue.FilePath()}, + Region: sarifRegion{StartLine: issue.Line(), StartColumn: issue.Column()}, + } + loc := sarifLocation{PhysicalLocation: physLoc} + + curResult := sarifResult{ + RuleID: issue.Text, + Level: issue.Severity, + Message: struct { + Text string "json:\"text\"" + }{Text: issue.Text}, + Locations: []sarifLocation{loc}, + } + + sr.Results = append(sr.Results, curResult) + } + res.Runs = append(res.Runs, sr) + } + + // res := SarifResult{ + // Issues: issues, + // Report: p.rd, + // } + // if res.Issues == nil { + // res.Issues = []result.Issue{} + // } + + return json.NewEncoder(p.w).Encode(res) +} diff --git a/pkg/printers/sarif_test.go b/pkg/printers/sarif_test.go new file mode 100644 index 000000000000..ab183e4d2fb1 --- /dev/null +++ b/pkg/printers/sarif_test.go @@ -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(nil, 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.4.json","runs":[{"tool":{"driver":{"name":"linter-a"}},"results":[{"ruleId":"some issue","level":"warning","message":{"text":"some issue"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/filea.go","index":0},"region":{"startLine":10,"startColumn":4}}}]},{"ruleId":"some issue 2","level":"error","message":{"text":"some issue 2"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/filec.go","index":0},"region":{"startLine":11,"startColumn":5}}}]}]},{"tool":{"driver":{"name":"linter-b"}},"results":[{"ruleId":"another issue","level":"error","message":{"text":"another issue"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/fileb.go","index":0},"region":{"startLine":300,"startColumn":9}}}]}]}]} +` + + assert.Equal(t, expected, buf.String()) +} From 40949ef1ae635f899335880605c9746b9d2e58a6 Mon Sep 17 00:00:00 2001 From: Sean Lewis Date: Thu, 16 May 2024 03:35:33 -0600 Subject: [PATCH 2/7] Missed a couple spots, confirmed working --- pkg/printers/printer.go | 2 ++ pkg/printers/sarif.go | 31 +++++++++++-------------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/pkg/printers/printer.go b/pkg/printers/printer.go index d2944340874a..53db01220e31 100644 --- a/pkg/printers/printer.go +++ b/pkg/printers/printer.go @@ -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) } diff --git a/pkg/printers/sarif.go b/pkg/printers/sarif.go index 47fe7bd99a9a..2c256ab8c1e9 100644 --- a/pkg/printers/sarif.go +++ b/pkg/printers/sarif.go @@ -4,7 +4,6 @@ import ( "encoding/json" "io" - "github.com/golangci/golangci-lint/pkg/report" "github.com/golangci/golangci-lint/pkg/result" ) @@ -57,26 +56,20 @@ type sarifRegion struct { } type Sarif struct { - rd *report.Data - w io.Writer + w io.Writer } -func NewSarif(rd *report.Data, w io.Writer) *Sarif { +func NewSarif(w io.Writer) *Sarif { return &Sarif{ - rd: rd, - w: w, + w: w, } } -// type SarifResult struct { -// Issues []result.Issue -// Report *report.Data -// } - func (p Sarif) Print(issues []result.Issue) error { res := SarifResult{} res.Version = sarifVersion res.Schema = sarifSchemaURI + res.Runs = []sarifRun{} toolMap := map[string][]result.Issue{} @@ -94,6 +87,12 @@ func (p Sarif) Print(issues []result.Issue) error { for i := range issues { issue := issues[i] + severity := issue.Severity + // set default to warning + if severity == "" { + severity = "warning" + } + physLoc := sarifPhysicalLocation{ ArtifactLocation: sarifArtifactLocation{URI: issue.FilePath()}, Region: sarifRegion{StartLine: issue.Line(), StartColumn: issue.Column()}, @@ -102,7 +101,7 @@ func (p Sarif) Print(issues []result.Issue) error { curResult := sarifResult{ RuleID: issue.Text, - Level: issue.Severity, + Level: severity, Message: struct { Text string "json:\"text\"" }{Text: issue.Text}, @@ -114,13 +113,5 @@ func (p Sarif) Print(issues []result.Issue) error { res.Runs = append(res.Runs, sr) } - // res := SarifResult{ - // Issues: issues, - // Report: p.rd, - // } - // if res.Issues == nil { - // res.Issues = []result.Issue{} - // } - return json.NewEncoder(p.w).Encode(res) } From 0d23e3d42fd96f1e0c8b708ef996fb84c8b50f8e Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Thu, 16 May 2024 14:09:01 +0200 Subject: [PATCH 3/7] review: golangci-lint as tool/drivers --- pkg/printers/sarif.go | 88 +++++++++++++++++--------------------- pkg/printers/sarif_test.go | 4 +- 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/pkg/printers/sarif.go b/pkg/printers/sarif.go index 2c256ab8c1e9..3b7c8b0e7b49 100644 --- a/pkg/printers/sarif.go +++ b/pkg/printers/sarif.go @@ -12,11 +12,12 @@ const ( sarifSchemaURI = "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json" ) -type SarifResult struct { +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"` @@ -29,14 +30,16 @@ type sarifTool struct { } type sarifResult struct { - RuleID string `json:"ruleId"` - Level string `json:"level"` - Message struct { - Text string `json:"text"` - } `json:"message"` + 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"` } @@ -50,6 +53,7 @@ type sarifArtifactLocation struct { URI string `json:"uri"` Index int `json:"index"` } + type sarifRegion struct { StartLine int `json:"startLine"` StartColumn int `json:"startColumn"` @@ -60,58 +64,44 @@ type Sarif struct { } func NewSarif(w io.Writer) *Sarif { - return &Sarif{ - w: w, - } + return &Sarif{w: w} } func (p Sarif) Print(issues []result.Issue) error { - res := SarifResult{} - res.Version = sarifVersion - res.Schema = sarifSchemaURI - res.Runs = []sarifRun{} + output := SarifOutput{ + Version: sarifVersion, + Schema: sarifSchemaURI, + } - toolMap := map[string][]result.Issue{} + run := sarifRun{} + run.Tool.Driver.Name = "golangci-lint" for i := range issues { issue := issues[i] - linter := issue.FromLinter - toolMap[linter] = append(toolMap[linter], issue) - } - for curtool, issues := range toolMap { - tool := sarifTool{} - tool.Driver.Name = curtool - sr := sarifRun{} - sr.Tool = tool - - for i := range issues { - issue := issues[i] - severity := issue.Severity - // set default to warning - if severity == "" { - severity = "warning" - } - - physLoc := sarifPhysicalLocation{ - ArtifactLocation: sarifArtifactLocation{URI: issue.FilePath()}, - Region: sarifRegion{StartLine: issue.Line(), StartColumn: issue.Column()}, - } - loc := sarifLocation{PhysicalLocation: physLoc} - - curResult := sarifResult{ - RuleID: issue.Text, - Level: severity, - Message: struct { - Text string "json:\"text\"" - }{Text: issue.Text}, - Locations: []sarifLocation{loc}, - } - - sr.Results = append(sr.Results, curResult) + 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()}, + }, + }, + }, } - res.Runs = append(res.Runs, sr) + + run.Results = append(run.Results, sr) } - return json.NewEncoder(p.w).Encode(res) + output.Runs = []sarifRun{run} + + return json.NewEncoder(p.w).Encode(output) } diff --git a/pkg/printers/sarif_test.go b/pkg/printers/sarif_test.go index ab183e4d2fb1..f9e591f7e27c 100644 --- a/pkg/printers/sarif_test.go +++ b/pkg/printers/sarif_test.go @@ -55,12 +55,12 @@ func TestSarif_Print(t *testing.T) { buf := new(bytes.Buffer) - printer := NewSarif(nil, buf) + 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.4.json","runs":[{"tool":{"driver":{"name":"linter-a"}},"results":[{"ruleId":"some issue","level":"warning","message":{"text":"some issue"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/filea.go","index":0},"region":{"startLine":10,"startColumn":4}}}]},{"ruleId":"some issue 2","level":"error","message":{"text":"some issue 2"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/filec.go","index":0},"region":{"startLine":11,"startColumn":5}}}]}]},{"tool":{"driver":{"name":"linter-b"}},"results":[{"ruleId":"another issue","level":"error","message":{"text":"another issue"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"path/to/fileb.go","index":0},"region":{"startLine":300,"startColumn":9}}}]}]}]} + expected := `{"version":"2.1.0","$schema":"https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.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()) From ef524c576c4268eeb800fedfe5f267e2d38816ce Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Thu, 16 May 2024 15:25:32 +0200 Subject: [PATCH 4/7] review: documentation --- .golangci.next.reference.yml | 1 + jsonschema/golangci.next.jsonschema.json | 3 ++- pkg/printers/sarif.go | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.golangci.next.reference.yml b/.golangci.next.reference.yml index 361b92c2faf9..db04d57ecc53 100644 --- a/.golangci.next.reference.yml +++ b/.golangci.next.reference.yml @@ -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. diff --git a/jsonschema/golangci.next.jsonschema.json b/jsonschema/golangci.next.jsonschema.json index 5b0082759294..ff0e6f4d3cf3 100644 --- a/jsonschema/golangci.next.jsonschema.json +++ b/jsonschema/golangci.next.jsonschema.json @@ -432,7 +432,8 @@ "code-climate", "junit-xml", "github-actions", - "teamcity" + "teamcity", + "sarif" ] } }, diff --git a/pkg/printers/sarif.go b/pkg/printers/sarif.go index 3b7c8b0e7b49..95b139b98623 100644 --- a/pkg/printers/sarif.go +++ b/pkg/printers/sarif.go @@ -92,7 +92,10 @@ func (p Sarif) Print(issues []result.Issue) error { { PhysicalLocation: sarifPhysicalLocation{ ArtifactLocation: sarifArtifactLocation{URI: issue.FilePath()}, - Region: sarifRegion{StartLine: issue.Line(), StartColumn: issue.Column()}, + Region: sarifRegion{ + StartLine: issue.Line(), + StartColumn: issue.Column(), + }, }, }, }, From ecbebb995bc610466dc02569841841087d47ac29 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 17 May 2024 15:30:43 +0200 Subject: [PATCH 5/7] review Co-authored-by: Oleksandr Redko --- pkg/printers/sarif.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/printers/sarif.go b/pkg/printers/sarif.go index 95b139b98623..621836866317 100644 --- a/pkg/printers/sarif.go +++ b/pkg/printers/sarif.go @@ -9,7 +9,7 @@ import ( const ( sarifVersion = "2.1.0" - sarifSchemaURI = "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json" + sarifSchemaURI = "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.6.json" ) type SarifOutput struct { From 50f705719d0110c548f634c3bb1692b8a3fb480a Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 17 May 2024 15:33:03 +0200 Subject: [PATCH 6/7] review: simplify --- pkg/printers/sarif.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/printers/sarif.go b/pkg/printers/sarif.go index 621836866317..378ee008aed8 100644 --- a/pkg/printers/sarif.go +++ b/pkg/printers/sarif.go @@ -68,11 +68,6 @@ func NewSarif(w io.Writer) *Sarif { } func (p Sarif) Print(issues []result.Issue) error { - output := SarifOutput{ - Version: sarifVersion, - Schema: sarifSchemaURI, - } - run := sarifRun{} run.Tool.Driver.Name = "golangci-lint" @@ -104,7 +99,11 @@ func (p Sarif) Print(issues []result.Issue) error { run.Results = append(run.Results, sr) } - output.Runs = []sarifRun{run} + output := SarifOutput{ + Version: sarifVersion, + Schema: sarifSchemaURI, + Runs: []sarifRun{run}, + } return json.NewEncoder(p.w).Encode(output) } From 9a3c9ccd3c4257748539ee18ee10f2bbaf86d182 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 17 May 2024 15:38:08 +0200 Subject: [PATCH 7/7] review: fix tests --- pkg/printers/sarif_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/printers/sarif_test.go b/pkg/printers/sarif_test.go index f9e591f7e27c..7ebda1210f2a 100644 --- a/pkg/printers/sarif_test.go +++ b/pkg/printers/sarif_test.go @@ -60,7 +60,7 @@ func TestSarif_Print(t *testing.T) { 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.4.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}}}]}]}]} + 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())