From ec736985a961bdace077b319d279cf79371e2fb9 Mon Sep 17 00:00:00 2001 From: Spyros Date: Mon, 18 Jul 2022 15:45:20 +0300 Subject: [PATCH] Sarif producer (#137) * make stdout_json consumer in go * init_sarif_producer * sarif producer * add tests * lint * lint * linting * fix build --- .plzconfig | 1 + producers/sarif/BUILD | 37 ++++++++ producers/sarif/Dockerfile | 5 + producers/sarif/main.go | 62 ++++++++++++ producers/sarif/main_test.go | 176 +++++++++++++++++++++++++++++++++++ third_party/go/BUILD | 9 ++ third_party/python/BUILD | 1 + 7 files changed, 291 insertions(+) create mode 100644 producers/sarif/BUILD create mode 100644 producers/sarif/Dockerfile create mode 100644 producers/sarif/main.go create mode 100644 producers/sarif/main_test.go diff --git a/.plzconfig b/.plzconfig index fd25340..861b17b 100644 --- a/.plzconfig +++ b/.plzconfig @@ -21,6 +21,7 @@ gotool = //third_party/lang:go_tool|go defaultinterpreter = python3 moduledir = third_party.python usepypi = true +disablevendorflags = true [proto] protoctool = //third_party/proto:protoc diff --git a/producers/sarif/BUILD b/producers/sarif/BUILD new file mode 100644 index 0000000..865b678 --- /dev/null +++ b/producers/sarif/BUILD @@ -0,0 +1,37 @@ +subinclude("//third_party/defs:docker") + +# this producer covers any tool that supports the SARIF format https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=sarif +go_binary( + name = "sarif", + srcs = [ + "main.go", + ], + deps = [ + "//api/proto:v1", + "//producers", + "//third_party/go:go-sarif", + ], +) + +go_test( + name = "sarif_test", + srcs = [ + "main.go", + "main_test.go", + ], + deps = [ + "//api/proto:v1", + "//producers", + "//third_party/go:go-sarif", + "//third_party/go:stretchr_testify", + ], +) + +docker_image( + name = "dracon-producer-sarif", + srcs = [ + ":sarif", + ], + base_image = "//build/docker:dracon-base-go", + image = "dracon-producer-sarif", +) diff --git a/producers/sarif/Dockerfile b/producers/sarif/Dockerfile new file mode 100644 index 0000000..0b3c7f5 --- /dev/null +++ b/producers/sarif/Dockerfile @@ -0,0 +1,5 @@ +FROM //build/docker:dracon-base-go + +COPY sarif /parse + +ENTRYPOINT ["/parse"] diff --git a/producers/sarif/main.go b/producers/sarif/main.go new file mode 100644 index 0000000..62b71c4 --- /dev/null +++ b/producers/sarif/main.go @@ -0,0 +1,62 @@ +package main + +import ( + v1 "github.com/thought-machine/dracon/api/proto/v1" + + "log" + + "github.com/owenrumney/go-sarif/v2/sarif" + "github.com/thought-machine/dracon/producers" +) + +func main() { + if err := producers.ParseFlags(); err != nil { + log.Fatal(err) + } + + inFile, err := producers.ReadInFile() + if err != nil { + log.Fatal(err) + } + + results, err := sarif.FromString(string(inFile)) + if err != nil { + log.Fatal(err) + } + for _, run := range results.Runs { + tool := run.Tool.Driver.Name + if err := producers.WriteDraconOut(tool, parseOut(*run)); err != nil { + log.Fatal(err) + } + } +} + +func parseOut(run sarif.Run) []*v1.Issue { + issues := []*v1.Issue{} + for _, res := range run.Results { + for _, loc := range res.Locations { + target := loc.PhysicalLocation.ArtifactLocation.URI + issues = append(issues, &v1.Issue{ + Target: *target, + Title: *res.RuleID, + Description: *res.Message.Text, + Type: "Security Automation Result", + Severity: LevelToSeverity(*res.Level), + Confidence: v1.Confidence_CONFIDENCE_MEDIUM, + Cvss: 0, + Cve: "", + }) + } + } + return issues +} + +// LevelToSeverity transforms error, warning and note levels to high, medium and low respectively +func LevelToSeverity(level string) v1.Severity { + if level == "error" { + return v1.Severity_SEVERITY_HIGH + } else if level == "warning" { + return v1.Severity_SEVERITY_MEDIUM + } + return v1.Severity_SEVERITY_LOW +} diff --git a/producers/sarif/main_test.go b/producers/sarif/main_test.go new file mode 100644 index 0000000..82befb9 --- /dev/null +++ b/producers/sarif/main_test.go @@ -0,0 +1,176 @@ +package main + +import ( + v1 "github.com/thought-machine/dracon/api/proto/v1" + + "testing" + + "github.com/owenrumney/go-sarif/v2/sarif" + "github.com/stretchr/testify/assert" +) + +func TestParseOut(t *testing.T) { + results, err := sarif.FromString(exampleOutput) + if err != nil { + t.Logf(err.Error()) + t.Fail() + } + + expectedIssues := []*v1.Issue{ + { + Target: "main.go", + Type: "Security Automation Result", + Title: "G404", + Severity: v1.Severity_SEVERITY_HIGH, + Confidence: v1.Confidence_CONFIDENCE_MEDIUM, + Description: "Use of weak random number generator (math/rand instead of crypto/rand)"}, + { + Target: "main.go", + Type: "Security Automation Result", + Title: "G104", + Severity: v1.Severity_SEVERITY_MEDIUM, + Confidence: v1.Confidence_CONFIDENCE_MEDIUM, + Description: "Errors unhandled."}, + } + for _, run := range results.Runs { + issues := parseOut(*run) + + assert.Equal(t, expectedIssues, issues) + } +} + +func TestParseOutTrivy(t *testing.T) { + results, err := sarif.FromString(trivyOutput) + if err != nil { + t.Logf(err.Error()) + t.Fail() + } + + expectedIssues := []*v1.Issue{ + { + Target: "library/ubuntu", + Type: "Security Automation Result", + Title: "CVE-2016-20013", + Severity: v1.Severity_SEVERITY_LOW, + Confidence: v1.Confidence_CONFIDENCE_MEDIUM, + Description: "Package: libc6\nInstalled Version: 2.35-0ubuntu3\nVulnerability CVE-2016-20013\nSeverity: LOW\nFixed Version: \nLink: [CVE-2016-20013](https://avd.aquasec.com/nvd/cve-2016-20013)", + }, + } + for _, run := range results.Runs { + issues := parseOut(*run) + + assert.Equal(t, expectedIssues, issues) + } +} + +var trivyOutput = `{ + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json", + "runs": [ + { + "tool": { + "driver": { + "fullName": "Trivy Vulnerability Scanner", + "informationUri": "https://github.com/aquasecurity/trivy", + "name": "Trivy", + "version": "0.29.2" + } + }, + "results": [ + { + "ruleId": "CVE-2016-20013", + "ruleIndex": 3, + "level": "note", + "message": { + "text": "Package: libc6\nInstalled Version: 2.35-0ubuntu3\nVulnerability CVE-2016-20013\nSeverity: LOW\nFixed Version: \nLink: [CVE-2016-20013](https://avd.aquasec.com/nvd/cve-2016-20013)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "library/ubuntu", + "uriBaseId": "ROOTPATH" + }, + "region": { + "startLine": 1, + "startColumn": 1, + "endLine": 1, + "endColumn": 1 + } + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "originalUriBaseIds": { + "ROOTPATH": { + "uri": "file:///" + } + } + } + ] + }` +var exampleOutput = `{ + "runs": [{ + "results": [{ + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "main.go" + }, + "region": { + "endColumn": 7, + "endLine": 83, + "snippet": { + "text": "r := rand.New(rand.NewSource(time.Now().UnixNano()))" + }, + "sourceLanguage": "go", + "startColumn": 7, + "startLine": 83 + } + } + }], + "message": { + "text": "Use of weak random number generator (math/rand instead of crypto/rand)" + }, + "ruleId": "G404" + }, + { + "level": "warning", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "main.go" + }, + "region": { + "endColumn": 2, + "endLine": 347, + "snippet": { + "text": "zipWriter.Close()" + }, + "sourceLanguage": "go", + "startColumn": 2, + "startLine": 347 + } + } + }], + "message": { + "text": "Errors unhandled." + }, + "ruleId": "G104", + "ruleIndex": 3 + } + ], + "tool": { + "driver": { + "guid": "8b518d5f-906d-39f9-894b-d327b1a421c5", + "informationUri": "https://github.com/securego/gosec/", + "name": "gosec" + } + } + }], + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0" +}` diff --git a/third_party/go/BUILD b/third_party/go/BUILD index 075d5f2..4a63736 100644 --- a/third_party/go/BUILD +++ b/third_party/go/BUILD @@ -693,3 +693,12 @@ go_module( module = "github.com/google/go-cmp", version = "v0.5.8", ) + +go_module( + name = "go-sarif", + hashes = ["sha1: 22c50e984ceddf7ea457e1f57ad6766c4f905df9"], + install = ["..."], + licences = ["Unlicense license"], + module = "github.com/owenrumney/go-sarif/v2", + version = "v2.1.2", +) diff --git a/third_party/python/BUILD b/third_party/python/BUILD index 1cb988c..f653c43 100644 --- a/third_party/python/BUILD +++ b/third_party/python/BUILD @@ -26,6 +26,7 @@ pip_library( "b3ce5a39751dd052c771fb6b840bf5c4eaeaa8356ed8549718eaab6acf06c0eb", # TM Mirror "841b1413fc9f13ed481b7c2df0d3a8c58879d405aa2f289a7998e78119ba1b73", # Public "ba8b2677634bce32cc743242ae5a2596c2f5c82ff1826b40ce0093a0f8d0b3c5", # GitHub Actions + "331ca5f64f49cba507b2dd66b0d327acf6dffa5acae53bad1e949e5e80b3fe5f", ], licences = ["MIT"], version = "40.4.3",