From 48297f1f2acd3539aafc0293e1c95b86d2bcf26a Mon Sep 17 00:00:00 2001 From: Taj Date: Sun, 5 Feb 2023 13:27:54 +0000 Subject: [PATCH] Proper sarif output --- cmd/vulnny/main.go | 30 +++++++++++++++++++++--------- internal/sarif/sarif.go | 37 +++++++++++++++++++++++++------------ internal/sarif/spec.go | 34 +++++++++++++++++----------------- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/cmd/vulnny/main.go b/cmd/vulnny/main.go index 51c03a3..bfd7634 100644 --- a/cmd/vulnny/main.go +++ b/cmd/vulnny/main.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "errors" "flag" "fmt" "go/token" @@ -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 | @@ -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 } diff --git a/internal/sarif/sarif.go b/internal/sarif/sarif.go index 5155148..a86daba 100644 --- a/internal/sarif/sarif.go +++ b/internal/sarif/sarif.go @@ -1,6 +1,7 @@ package sarif import ( + "go/build" "os" "strings" @@ -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 { @@ -26,9 +23,13 @@ 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{ @@ -36,11 +37,11 @@ func FromResult(r *vulncheck.Result) (*Log, error) { StartColumn: cs.Pos.Column, } pLoc := PhysicalLocation{ - ArtifactLocation: aLoc, - Region: region, + ArtifactLocation: &aLoc, + Region: ®ion, } loc := Location{ - PhysicalLocation: pLoc, + PhysicalLocation: &pLoc, } locations = append(locations, loc) } @@ -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 +} diff --git a/internal/sarif/spec.go b/internal/sarif/spec.go index ec5a3c6..74658cd 100644 --- a/internal/sarif/spec.go +++ b/internal/sarif/spec.go @@ -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. } @@ -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. @@ -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 @@ -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"`