Skip to content
This repository has been archived by the owner on Aug 14, 2024. It is now read-only.

Commit

Permalink
Proper sarif output
Browse files Browse the repository at this point in the history
  • Loading branch information
tjgurwara99 committed Feb 5, 2023
1 parent 0c61880 commit 48297f1
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 38 deletions.
30 changes: 21 additions & 9 deletions cmd/vulnny/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"go/token"
Expand All @@ -16,6 +17,20 @@ import (

func main() {
flag.Parse()
log, err := runVulnny()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
data, err := json.MarshalIndent(log, "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to marshal SARIF: %s\n", err.Error())
os.Exit(1)
}
fmt.Println(string(data))
}

func runVulnny() (*sarif.Log, error) {
fset := token.NewFileSet()
const mode packages.LoadMode = packages.NeedName | packages.NeedImports | packages.NeedTypes |
packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps |
Expand All @@ -26,29 +41,26 @@ func main() {
}
pkgs, err := packages.Load(&cfg, flag.Args()...)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load packages: %s", err.Error())
os.Exit(1)
return nil, fmt.Errorf("failed to load packages: %w", err)
}
if packages.PrintErrors(pkgs) > 0 {
os.Exit(1)
return nil, errors.New("package loaded with errors")
}
vPkgs := vulncheck.Convert(pkgs)
sources := []string{"https://vuln.go.dev"}
dbClient, err := client.NewClient(sources, client.Options{})
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create a client: %s", err.Error())
return nil, fmt.Errorf("failed to create a client: %w", err)
}
res, err := vulncheck.Source(context.Background(), vPkgs, &vulncheck.Config{
Client: dbClient,
})
if err != nil {
fmt.Fprintf(os.Stderr, "failed to run source analysis: %v", err)
os.Exit(1)
return nil, fmt.Errorf("failed to run source analysis: %w", err)
}
log, err := sarif.FromResult(res)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to convert Result to sarif log: %s", err.Error())
return nil, fmt.Errorf("failed to convert Result to sarif log: %w", err.Error())
}
data, _ := json.MarshalIndent(log, "", " ")
fmt.Println(string(data))
return log, nil
}
37 changes: 25 additions & 12 deletions internal/sarif/sarif.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sarif

import (
"go/build"
"os"
"strings"

Expand All @@ -12,10 +13,6 @@ func FromResult(r *vulncheck.Result) (*Log, error) {
filtered := slice.Filter(r.Vulns, func(i *vulncheck.Vuln) bool {
return i.CallSink != 0
})
wd, err := os.Getwd()
if err != nil {
return nil, err
}
var j int
var results []Result
for i, v := range filtered {
Expand All @@ -26,21 +23,25 @@ func FromResult(r *vulncheck.Result) (*Log, error) {
}
var locations []Location
for _, cs := range fn.CallSites {
uri, skip := getURI(cs.Pos.Filename)
if skip {
continue
}
aLoc := ArtifactLocation{
URIBaseID: "%SRCROOT%",
URI: strings.TrimPrefix(cs.Pos.Filename, wd+"/"),
URI: uri,
Index: i - j,
}
region := Region{
StartLine: cs.Pos.Line,
StartColumn: cs.Pos.Column,
}
pLoc := PhysicalLocation{
ArtifactLocation: aLoc,
Region: region,
ArtifactLocation: &aLoc,
Region: &region,
}
loc := Location{
PhysicalLocation: pLoc,
PhysicalLocation: &pLoc,
}
locations = append(locations, loc)
}
Expand All @@ -50,29 +51,41 @@ func FromResult(r *vulncheck.Result) (*Log, error) {
level := LevelError
ruleID := v.OSV.ID
results = append(results, Result{
Message: message,
Message: &message,
Level: level,
RuleID: ruleID,
Locations: locations,
})
}

tool := Tool{
Driver: ToolComponent{
Name: "Vulnny",
},
}

runs := []Run{
{
Tool: tool,
Results: results,
},
}

return &Log{
Version: Version,
Schema: Schema,
Runs: runs,
}, nil
}

func getURI(filename string) (_ string, skip bool) {
gopath := os.Getenv("GOPATH")
if gopath == "" {
gopath = build.Default.GOPATH
}
if strings.HasPrefix(filename, gopath) {
return "", true
}
wd, err := os.Getwd()
if err != nil {
return "", true
}
return strings.TrimPrefix(filename, wd+"/"), false
}
34 changes: 17 additions & 17 deletions internal/sarif/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ type MultiFormatMessageString struct {
}

type ToolComponent struct {
Name string `json:"name"`
GUID string `json:"guid,omitempty"`
SemanitcVersion string `json:"semanticVersion,omitempty"`
Language string `json:"language,omitempty"`
ShortDescription MultiFormatMessageString `json:"shortDescription,omitempty"`
FullDescription MultiFormatMessageString `json:"fullDescription,omitempty"`
Name string `json:"name"`
GUID string `json:"guid,omitempty"`
SemanitcVersion string `json:"semanticVersion,omitempty"`
Language string `json:"language,omitempty"`
ShortDescription *MultiFormatMessageString `json:"shortDescription,omitempty"`
FullDescription *MultiFormatMessageString `json:"fullDescription,omitempty"`
// there are some other fields that might be useful
// but at this stage it would be overengineering.
}
Expand All @@ -48,10 +48,10 @@ const (
)

type ArtifactLocation struct {
URI string `json:"uri"`
URIBaseID string `json:"uriBaseId,omitempty"`
Index int `json:"index,omitempty"`
Description Message `json:"description,omitempty"`
URI string `json:"uri"`
URIBaseID string `json:"uriBaseId,omitempty"`
Index int `json:"index,omitempty"`
Description *Message `json:"description,omitempty"`
}

// Snippet object represents a portion of the artifact that is relevant to the result.
Expand All @@ -69,9 +69,9 @@ type Region struct {
}

type PhysicalLocation struct {
ArtifactLocation ArtifactLocation `json:"artifactLocation,omitempty"`
Region Region `json:"region,omitempty"`
ContextRegion Region `json:"contextRegion,omitempty"`
ArtifactLocation *ArtifactLocation `json:"artifactLocation,omitempty"`
Region *Region `json:"region,omitempty"`
ContextRegion *Region `json:"contextRegion,omitempty"`
}

type LogicationLocationKind string
Expand Down Expand Up @@ -99,18 +99,18 @@ type LogicalLocation struct {
}

type Location struct {
ID int `json:"id,omitempty"`
PhysicalLocation PhysicalLocation `json:"physicalLocation,omitempty"`
ID int `json:"id,omitempty"`
PhysicalLocation *PhysicalLocation `json:"physicalLocation,omitempty"`
// This might be something that is not necessary for the current version of the tool.
// LogicalLocation LogicalLocation `json:"logicalLocation,omitempty"`
Message Message `json:"message,omitempty"`
Message *Message `json:"message,omitempty"`
Annotations []Region `json:"annotations,omitempty"`
// Relationships might be useful in the future but for now we'll leave it out.
// Relationships []Relationship `json:"relationships,omitempty"`
}

type Result struct {
Message Message `json:"message"`
Message *Message `json:"message"`
RuleID string `json:"ruleId,omitempty"`
RuleIndex int `json:"ruleIndex,omitempty"` // default -1 & minimum -1
Level Level `json:"level,omitempty"`
Expand Down

0 comments on commit 48297f1

Please sign in to comment.