Skip to content

Commit

Permalink
Merge pull request #20 from owenrumney/cleanup-documentation
Browse files Browse the repository at this point in the history
Update the documentation and add an example
  • Loading branch information
owenrumney authored Jun 7, 2021
2 parents 91aa283 + 7c39e28 commit 37e6198
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 27 deletions.
38 changes: 11 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,42 +81,26 @@ For more information about SARIF, you can visit the [Oasis Open](https://www.oas

## Usage

Add an import to `go get github.com/owenrumney/go-sarif/sarif`
### Parsing a Sarif report

###
There are a number of ways to load in the content of a sarif report.

#### Open

Add an import to `github.com/owenrumney/go-sarif/sarif`
`sarif.Open` takes a file path and loads the sarif from that location. Returns a report and any corresponding error

Creating a new Sarif report is done by passing the version, the only supported at the moment is `2.1.0`

The example below is taken from the `tfsec` usage of `go-sarif`. For context, at the end of the process a slice of `Result` objects is returned with the relevant information about the check failures.
#### FromBytes

```go
// create the report object
report, err := sarif.New(sarif.Version210)
if err != nil {
return err
}
`sarif.FromBytes` takes a slice of byte and returns a report and any corresponding error.

// add a run to the report
run := report.AddRun("tfsec", "https://tfsec.dev")
#### FromString

// for each result add the rule, location and result to the report
for _, result := range results {
rule := run.AddRule(string(result.RuleID)).
WithDescription(result.Description).
WithHelpUri(fmt.Sprintf("https://tfsec.dev/%s/%s", strings.ToLower(string(result.RuleProvider)), result.RuleID))
`sarif.FromString` takes a string of the sarif content and returns a report and any corresponding error.

ruleResult := run.AddResult(rule.Id).
WithMessage(string(result.RuleDescription)).
WithLevel(string(result.Severity)).
WithLocationDetails(result.Range.Filename, result.Range.StartLine, 1)
### Creating a new report

run.AddResultDetails(rule, ruleResult, result.Range.Filename)
}
Creating a new Sarif report is done by passing the version, the only supported at the moment is `2.1.0`

// print the report to anything that implements `io.Writer`
return report.Write(w)
```
for a detailed example check the example folder [example/main.go](example/main.go)

5 changes: 5 additions & 0 deletions example/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/owenrumney/go-sarif/example

go 1.16

require github.com/owenrumney/go-sarif v1.0.6
31 changes: 31 additions & 0 deletions example/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/owenrumney/go-sarif v1.0.6 h1:gmlbOaCf3Ya6afS67moMmz/P0F5N7SVgIvH6IZDi570=
github.com/owenrumney/go-sarif v1.0.6/go.mod h1:sgJM0ZaZ28jT8t8Iq3/mUCFBW9cX09EobIBXYOhiYBc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/zclconf/go-cty v1.8.3 h1:48gwZXrdSADU2UW9eZKHprxAI7APZGW9XmExpJpSjT0=
github.com/zclconf/go-cty v1.8.3/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
102 changes: 102 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

import (
"encoding/json"
"io/ioutil"
"os"
"strings"

"github.com/owenrumney/go-sarif/sarif"
)

// simple structure for the output of tfsec
type TfsecResults struct {
Results []struct {
RuleID string `json:"rule_id"`
RuleDescription string `json:"rule_description"`
RuleProvider string `json:"rule_provider"`
Link string `json:"link"`
Location struct {
Filename string `json:"filename"`
StartLine int `json:"start_line"`
EndLine int `json:"end_line"`
} `json:"location"`
Description string `json:"description"`
Impact string `json:"impact"`
Resolution string `json:"resolution"`
Severity string `json:"severity"`
Passed bool `json:"passed"`
} `json:"results"`
}

func main() {

// Get the results from the results file
tfsecResults, err := loadTfsecResults()
if err != nil {
panic(err)
}

// create a new report object
report, err := sarif.New(sarif.Version210)
if err != nil {
panic(err)
}

// create a run for tfsec
run := sarif.NewRun("tfsec", "https://tfsec.dev")

// for each result, add the
for _, r := range tfsecResults.Results {

// create a property bag for the non standard stuff
pb := sarif.NewPropertyBag()
pb.Add("impact", r.Impact)
pb.Add("resolution", r.Resolution)

// create a new rule for each rule id
run.AddRule(r.RuleID).
WithDescription(r.Description).
WithHelp(r.Link).
WithProperties(pb.Properties)

// add each of the results with the details of where the issue occurred
run.AddResult(r.RuleID).
WithLevel(strings.ToLower(r.Severity)).
WithMessage(sarif.NewTextMessage(r.Description)).
WithLocation(
sarif.NewLocationWithPhysicalLocation(
sarif.NewPhysicalLocation().
WithArtifactLocation(
sarif.NewSimpleArtifactLocation(r.Location.Filename),
).WithRegion(
sarif.NewSimpleRegion(r.Location.StartLine, r.Location.EndLine),
),
),
)
}

// add the run to the report
report.AddRun(run)

// print the report to stdout
report.PrettyWrite(os.Stdout)

// save the report
// report.WriteFile("example-report.sarif")

}

// load the example results file
func loadTfsecResults() (TfsecResults, error) {

jsonResult, err := ioutil.ReadFile("results.json")
if err != nil {
panic(err)
}

var results TfsecResults

err = json.Unmarshal(jsonResult, &results)
return results, err
}
148 changes: 148 additions & 0 deletions example/results.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"results": [
{
"rule_id": "AWS006",
"rule_description": "An ingress security group rule allows traffic from /0.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS006/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 4,
"end_line": 4
},
"description": "Resource 'aws_security_group_rule.my-rule' defines a fully open ingress security group rule.",
"impact": "Your port exposed to the internet",
"resolution": "Set a more restrictive cidr range",
"severity": "WARNING",
"passed": false
},
{
"rule_id": "AZU003",
"rule_description": "Unencrypted managed disk.",
"rule_provider": "azure",
"link": "See https://tfsec.dev/docs/azure/AZU003/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 22,
"end_line": 22
},
"description": "Resource 'azurerm_managed_disk.source' defines an unencrypted managed disk.",
"impact": "",
"resolution": "",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS025",
"rule_description": "API Gateway domain name uses outdated SSL/TLS protocols.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS025/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 26,
"end_line": 27
},
"description": "Resource 'aws_api_gateway_domain_name.missing_security_policy' should include security_policy (defauls to outdated SSL/TLS policy).",
"impact": "Outdated SSL policies increase exposure to known vulnerabilites",
"resolution": "Use the most modern TLS/SSL policies available",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS025",
"rule_description": "API Gateway domain name uses outdated SSL/TLS protocols.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS025/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 30,
"end_line": 30
},
"description": "Resource 'aws_api_gateway_domain_name.empty_security_policy' defines outdated SSL/TLS policies (not using TLS_1_2).",
"impact": "Outdated SSL policies increase exposure to known vulnerabilites",
"resolution": "Use the most modern TLS/SSL policies available",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS025",
"rule_description": "API Gateway domain name uses outdated SSL/TLS protocols.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS025/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 34,
"end_line": 34
},
"description": "Resource 'aws_api_gateway_domain_name.outdated_security_policy' defines outdated SSL/TLS policies (not using TLS_1_2).",
"impact": "Outdated SSL policies increase exposure to known vulnerabilites",
"resolution": "Use the most modern TLS/SSL policies available",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS018",
"rule_description": "Missing description for security group/security group rule.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS018/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 2,
"end_line": 5
},
"description": "Resource 'aws_security_group_rule.my-rule' should include a description for auditing purposes.",
"impact": "Descriptions provide context for the firewall rule reasons",
"resolution": "Add descriptions for all security groups anf rules",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS004",
"rule_description": "Use of plain HTTP.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS004/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 9,
"end_line": 9
},
"description": "Resource 'aws_alb_listener.my-alb-listener' uses plain HTTP instead of HTTPS.",
"impact": "Your traffic is not protected",
"resolution": "Switch to HTTPS to benefit from TLS security features",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS003",
"rule_description": "AWS Classic resource usage.",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS003/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 12,
"end_line": 14
},
"description": "Resource 'aws_db_security_group.my-group' uses EC2 Classic. Use a VPC instead.",
"impact": "Classic resources are running in a shared environment with other customers",
"resolution": "Switch to VPC resources",
"severity": "ERROR",
"passed": false
},
{
"rule_id": "AWS092",
"rule_description": "DynamoDB tables should use at rest encyption with a Customer Managed Key",
"rule_provider": "aws",
"link": "See https://tfsec.dev/docs/aws/AWS092/ for more information.",
"location": {
"filename": "/home/billybob/supertfsec/example/main.tf",
"start_line": 41,
"end_line": 56
},
"description": "Resource 'aws_dynamodb_table.bad_example' is not using KMS CMK for encryption",
"impact": "Using AWS managed keys does not allow for fine grained control",
"resolution": "Enable server side encrytion with a customer managed key",
"severity": "WARNING",
"passed": false
}
]
}
9 changes: 9 additions & 0 deletions sarif/sarif.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ func getVersionSchema(version Version) (string, error) {
return "", fmt.Errorf("version [%s] is not supported", version)
}

// WriteFile will write the report to a file using a pretty formatter
func (sarif *Report) WriteFile(filename string) error {
file, err := os.OpenFile(filename, os.O_CREATE, 0744)
if err != nil {
return err
}
return sarif.PrettyWrite(file)
}

// Write writes the JSON as a string with no formatting
func (sarif *Report) Write(w io.Writer) error {
for _, run := range sarif.Runs {
Expand Down

0 comments on commit 37e6198

Please sign in to comment.