Skip to content

Commit

Permalink
Merge pull request #1807 from reviewdog/output-sarif
Browse files Browse the repository at this point in the history
Support -reporter=sarif
  • Loading branch information
haya14busa authored Jul 2, 2024
2 parents 24443e7 + 17fea07 commit b979fe0
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 1 deletion.
28 changes: 28 additions & 0 deletions .github/workflows/reviewdog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,34 @@ jobs:
cat ./_testdata/custom_rdjson.json | \
reviewdog -name="custom-rdjson" -f=rdjson -reporter=github-pr-annotations
reviewdog-sarif:
permissions:
contents: read
name: reviewdog (sarif)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Setup reviewdog
# uses: reviewdog/action-setup@v1
run: |
go install ./cmd/reviewdog
- name: Custom rdjson test
run: |
mkdir ../results
cat ./_testdata/custom_rdjson.json |
reviewdog -name="custom-rdjson" -f=rdjson -reporter=sarif |
tee ../results/custom-rdjson.sarif
- name: Install linters
run: go install golang.org/x/lint/golint@latest
- name: Run golint
run: |
golint ./... | reviewdog -f=golint -reporter=sarif |
tee ../results/golint.sarif
- uses: github/codeql-action/upload-sarif@v3

typos:
name: runner / typos
runs-on: ubuntu-latest
Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
outdated comments in github-pr-review reporter. Note that it won't delete
comments if there is a reply considering there can be a meaningful
discussion.
- [#1806](https://github.com/reviewdog/reviewdog/pull/1806) Add -reporter=rdjson/rdjsonl which outputs rdjson/rdjsonl format to stdout. It also changes the default behavior of -diff and -filter-mode for local reporters. If -filter-mode is not provided (-filter-mode=default) and -diff flag is not provided, reviewdog automatically set -filter-mode=nofilter.
- [#1806](https://github.com/reviewdog/reviewdog/pull/1806) Add
-reporter=rdjson/rdjsonl which outputs rdjson/rdjsonl format to stdout. It
also changes the default behavior of -diff and -filter-mode for local
reporters. If -filter-mode is not provided (-filter-mode=default) and -diff
flag is not provided, reviewdog automatically set -filter-mode=nofilter.
- [#1807](https://github.com/reviewdog/reviewdog/pull/1807) Add -reporter=sarif
which outputs SARIF format to stdout. You can upload the output SARIF to
GitHub and see code scanning alerts.


### :bug: Fixes
- ...
Expand Down
10 changes: 10 additions & 0 deletions cmd/reviewdog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ const (
"rdjsonl"
Report results to stdout in rdjsonl format.
"sarif"
Report results to stdout in SARIF format.
"github-check"
Report results to GitHub Check. It works both for Pull Requests and commits.
For Pull Request, you can see report results in GitHub PullRequest Check
Expand Down Expand Up @@ -452,6 +455,13 @@ func run(r io.Reader, w io.Writer, opt *option) error {
}
ds = d
cs = reviewdog.NewRDJSONLCommentWriter(w)
case "sarif":
d, err := localDiffService(opt)
if err != nil {
return err
}
ds = d
cs = reviewdog.NewSARIFCommentWriter(w, toolName(opt))
}

if isProject {
Expand Down
137 changes: 137 additions & 0 deletions comment_iowriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package reviewdog

import (
"context"
"encoding/json"
"fmt"
"io"

"github.com/haya14busa/go-sarif/sarif"
"github.com/reviewdog/reviewdog/proto/rdf"
"google.golang.org/protobuf/encoding/protojson"
)
Expand Down Expand Up @@ -142,3 +144,138 @@ func (cw *RDJSONCommentWriter) Flush(_ context.Context) error {
}
return nil
}

var _ CommentService = &SARIFCommentWriter{}

// SARIFCommentWriter
type SARIFCommentWriter struct {
w io.Writer
comments []*Comment
toolName string
}

func NewSARIFCommentWriter(w io.Writer, toolName string) *SARIFCommentWriter {
return &SARIFCommentWriter{w: w, toolName: toolName}
}

func (cw *SARIFCommentWriter) Post(_ context.Context, c *Comment) error {
cw.comments = append(cw.comments, c)
return nil
}

func (cw *SARIFCommentWriter) Flush(_ context.Context) error {
run := sarif.Run{
Tool: sarif.Tool{
Driver: sarif.ToolComponent{
Name: cw.toolName,
Rules: make([]sarif.ReportingDescriptor, 0),
},
},
}
seenRules := make(map[string]bool)
for _, c := range cw.comments {
result := sarif.Result{
Message: sarif.Message{
Text: sarif.String(c.Result.Diagnostic.Message),
},
}
if code := c.Result.Diagnostic.GetCode(); code.GetValue() != "" {
result.RuleID = sarif.String(code.GetValue())
if seen := seenRules[code.GetValue()]; !seen {
seenRules[code.GetValue()] = true
rd := sarif.ReportingDescriptor{
ID: code.GetValue(),
}
if code.GetUrl() != "" {
rd.HelpURI = sarif.String(code.GetUrl())
}
run.Tool.Driver.Rules = append(run.Tool.Driver.Rules, rd)
}
}
level := severity2level(c.Result.Diagnostic.GetSeverity())
if level != sarif.None {
result.Level = &level
}
artifactLoc := sarif.ArtifactLocation{
URI: sarif.String(c.Result.Diagnostic.GetLocation().GetPath()),
}
result.Locations = []sarif.Location{{
PhysicalLocation: &sarif.PhysicalLocation{
ArtifactLocation: &artifactLoc,
Region: range2region(c.Result.Diagnostic.GetLocation().GetRange()),
},
}}
if len(c.Result.Diagnostic.GetSuggestions()) > 0 {
result.Fixes = make([]sarif.Fix, 0)
for _, suggestion := range c.Result.Diagnostic.GetSuggestions() {
result.Fixes = append(result.Fixes, sarif.Fix{
ArtifactChanges: []sarif.ArtifactChange{
{
ArtifactLocation: artifactLoc,
Replacements: []sarif.Replacement{{
DeletedRegion: *range2region(suggestion.GetRange()),
InsertedContent: &sarif.ArtifactContent{
Text: sarif.String(suggestion.GetText()),
},
}},
},
},
})
}
}
if len(c.Result.Diagnostic.GetRelatedLocations()) > 0 {
result.RelatedLocations = make([]sarif.Location, 0)
for _, relLoc := range c.Result.Diagnostic.GetRelatedLocations() {
result.RelatedLocations = append(result.RelatedLocations, sarif.Location{
PhysicalLocation: &sarif.PhysicalLocation{
ArtifactLocation: &sarif.ArtifactLocation{
URI: sarif.String(relLoc.GetLocation().GetPath()),
},
Region: range2region(relLoc.GetLocation().GetRange()),
},
Message: &sarif.Message{
Text: sarif.String(relLoc.Message),
},
})
}
}
run.Results = append(run.Results, result)
}
slf := sarif.NewSarif()
slf.Runs = []sarif.Run{run}
encoder := json.NewEncoder(cw.w)
encoder.SetIndent("", " ")
return encoder.Encode(slf)
}

func range2region(rng *rdf.Range) *sarif.Region {
region := &sarif.Region{}
start := rng.GetStart()
end := rng.GetEnd()
if start.GetLine() > 0 {
region.StartLine = sarif.Int64(int64(start.GetLine()))
}
if start.GetColumn() > 0 {
// Column is not usually unicodeCodePoints, but let's just keep it
// as is...
region.StartColumn = sarif.Int64(int64(start.GetColumn()))
}
if end.GetLine() > 0 {
region.EndLine = sarif.Int64(int64(end.GetLine()))
}
if end.GetColumn() > 0 {
region.EndColumn = sarif.Int64(int64(end.GetColumn()))
}
return region
}

func severity2level(s rdf.Severity) sarif.Level {
switch s {
case rdf.Severity_ERROR:
return sarif.Error
case rdf.Severity_WARNING:
return sarif.Warning
default:
return sarif.None
}
}
123 changes: 123 additions & 0 deletions comment_iowriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,126 @@ func TestRDJSONCommentWriter_Post(t *testing.T) {
t.Errorf("got\n%v\nwant:\n%v", got, want)
}
}

func TestSARIFCommentWriter_Post(t *testing.T) {
comments := []*Comment{
{
Result: &filter.FilteredDiagnostic{
Diagnostic: &rdf.Diagnostic{
Location: &rdf.Location{Path: "/path/to/file"},
Message: "message",
},
},
ToolName: "tool name",
},
{
Result: &filter.FilteredDiagnostic{
Diagnostic: &rdf.Diagnostic{
Location: &rdf.Location{
Path: "/path/to/file",
Range: &rdf.Range{Start: &rdf.Position{
Column: 14,
}},
},
Message: "message",
},
},
},
{
Result: &filter.FilteredDiagnostic{
Diagnostic: &rdf.Diagnostic{
Location: &rdf.Location{
Path: "/path/to/file",
Range: &rdf.Range{Start: &rdf.Position{
Column: 14,
}},
},
Message: "message",
Source: &rdf.Source{
Name: "tool name in Diagnostic",
Url: "tool url",
},
},
},
},
}
buf := new(bytes.Buffer)
cw := NewSARIFCommentWriter(buf, "tool name [constructor]")
for _, c := range comments {
if err := cw.Post(context.Background(), c); err != nil {
t.Error(err)
}
}
if err := cw.Flush(context.Background()); err != nil {
t.Error(err)
}
want := `
{
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
"runs": [
{
"results": [
{
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "/path/to/file"
},
"region": {}
}
}
],
"message": {
"text": "message"
}
},
{
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "/path/to/file"
},
"region": {
"startColumn": 14
}
}
}
],
"message": {
"text": "message"
}
},
{
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "/path/to/file"
},
"region": {
"startColumn": 14
}
}
}
],
"message": {
"text": "message"
}
}
],
"tool": {
"driver": {
"name": "tool name [constructor]"
}
}
}
],
"version": "2.1.0"
}`
got := strings.TrimSpace(buf.String())
if got != strings.TrimSpace(want) {
t.Errorf("got\n%v\nwant:\n%v", got, want)
}
}

0 comments on commit b979fe0

Please sign in to comment.