From b94bb5ae9ee751a86f9ef2cbc44b1113bc2993de Mon Sep 17 00:00:00 2001 From: Owen Rumney Date: Fri, 10 Dec 2021 15:03:47 +0000 Subject: [PATCH] create v2 (#32) * create v2 * add documentation * update v2 documentation * update run to be complete --- example/example-report.sarif | 31 +- example/main.go | 12 +- go.mod | 3 + go.sum | 1 + v2/go.mod | 9 + v2/go.sum | 39 ++ v2/sarif/address.go | 77 ++++ v2/sarif/address_test.go | 29 ++ v2/sarif/artifact.go | 115 ++++++ v2/sarif/artifact_change.go | 21 + v2/sarif/artifact_change_test.go | 26 ++ v2/sarif/artifact_content.go | 32 ++ v2/sarif/artifact_content_test.go | 16 + v2/sarif/artifact_location.go | 63 +++ v2/sarif/artifact_location_test.go | 17 + v2/sarif/artifact_test.go | 1 + v2/sarif/attachment.go | 55 +++ v2/sarif/code_flow.go | 49 +++ v2/sarif/configuration_override.go | 25 ++ v2/sarif/conversion.go | 26 ++ v2/sarif/edge.go | 47 +++ v2/sarif/edge_traversal.go | 56 +++ v2/sarif/exception.go | 44 +++ v2/sarif/external_properties.go | 216 ++++++++++ v2/sarif/external_property_file_reference.go | 26 ++ v2/sarif/external_property_file_references.go | 189 +++++++++ v2/sarif/fix.go | 48 +++ v2/sarif/fix_test.go | 26 ++ v2/sarif/graph.go | 60 +++ v2/sarif/graph_traversal.go | 76 ++++ v2/sarif/invocation.go | 218 ++++++++++ v2/sarif/invocation_test.go | 24 ++ v2/sarif/location.go | 92 +++++ v2/sarif/location_relationship.go | 52 +++ v2/sarif/location_relationship_test.go | 18 + v2/sarif/location_test.go | 22 ++ v2/sarif/logical_location.go | 55 +++ v2/sarif/logical_location_test.go | 1 + v2/sarif/message.go | 54 +++ v2/sarif/message_test.go | 1 + v2/sarif/multi_format_message_string.go | 33 ++ v2/sarif/multi_format_message_string_test.go | 22 ++ v2/sarif/node.go | 52 +++ v2/sarif/notification.go | 98 +++++ v2/sarif/physical_location.go | 38 ++ v2/sarif/physical_location_test.go | 1 + v2/sarif/properties.go | 41 ++ v2/sarif/rectangle.go | 64 +++ v2/sarif/region.go | 113 ++++++ v2/sarif/region_test.go | 1 + v2/sarif/replacement.go | 21 + v2/sarif/replacement_test.go | 1 + v2/sarif/reporting_configuration.go | 39 ++ v2/sarif/reporting_descriptor.go | 92 +++++ v2/sarif/reporting_descriptor_reference.go | 40 ++ .../reporting_descriptor_reference_test.go | 1 + v2/sarif/result.go | 267 +++++++++++++ v2/sarif/result_provenance.go | 60 +++ v2/sarif/result_test.go | 1 + v2/sarif/run.go | 372 ++++++++++++++++++ v2/sarif/run_automation_details.go | 57 +++ v2/sarif/sarif.go | 115 ++++++ v2/sarif/special_locations.go | 18 + v2/sarif/stack.go | 49 +++ v2/sarif/stack_frame.go | 44 +++ v2/sarif/suppression.go | 42 ++ v2/sarif/test_helpers.go | 11 + v2/sarif/thread_flow.go | 69 ++++ v2/sarif/thread_flow_location.go | 114 ++++++ v2/sarif/tool.go | 21 + v2/sarif/tool_component.go | 82 ++++ v2/sarif/tool_component_reference.go | 33 ++ v2/sarif/translation_metadata.go | 90 +++++ v2/sarif/version_control_details.go | 55 +++ v2/sarif/web_request.go | 77 ++++ v2/sarif/web_response.go | 71 ++++ v2/test/report_stage_test.go | 90 +++++ v2/test/report_test.go | 124 ++++++ v2/test/run_stage_test.go | 50 +++ v2/test/run_test.go | 19 + 80 files changed, 4530 insertions(+), 30 deletions(-) create mode 100644 v2/go.mod create mode 100644 v2/go.sum create mode 100644 v2/sarif/address.go create mode 100644 v2/sarif/address_test.go create mode 100644 v2/sarif/artifact.go create mode 100644 v2/sarif/artifact_change.go create mode 100644 v2/sarif/artifact_change_test.go create mode 100644 v2/sarif/artifact_content.go create mode 100644 v2/sarif/artifact_content_test.go create mode 100644 v2/sarif/artifact_location.go create mode 100644 v2/sarif/artifact_location_test.go create mode 100644 v2/sarif/artifact_test.go create mode 100644 v2/sarif/attachment.go create mode 100644 v2/sarif/code_flow.go create mode 100644 v2/sarif/configuration_override.go create mode 100644 v2/sarif/conversion.go create mode 100644 v2/sarif/edge.go create mode 100644 v2/sarif/edge_traversal.go create mode 100644 v2/sarif/exception.go create mode 100644 v2/sarif/external_properties.go create mode 100644 v2/sarif/external_property_file_reference.go create mode 100644 v2/sarif/external_property_file_references.go create mode 100644 v2/sarif/fix.go create mode 100644 v2/sarif/fix_test.go create mode 100644 v2/sarif/graph.go create mode 100644 v2/sarif/graph_traversal.go create mode 100644 v2/sarif/invocation.go create mode 100644 v2/sarif/invocation_test.go create mode 100644 v2/sarif/location.go create mode 100644 v2/sarif/location_relationship.go create mode 100644 v2/sarif/location_relationship_test.go create mode 100644 v2/sarif/location_test.go create mode 100644 v2/sarif/logical_location.go create mode 100644 v2/sarif/logical_location_test.go create mode 100644 v2/sarif/message.go create mode 100644 v2/sarif/message_test.go create mode 100644 v2/sarif/multi_format_message_string.go create mode 100644 v2/sarif/multi_format_message_string_test.go create mode 100644 v2/sarif/node.go create mode 100644 v2/sarif/notification.go create mode 100644 v2/sarif/physical_location.go create mode 100644 v2/sarif/physical_location_test.go create mode 100644 v2/sarif/properties.go create mode 100644 v2/sarif/rectangle.go create mode 100644 v2/sarif/region.go create mode 100644 v2/sarif/region_test.go create mode 100644 v2/sarif/replacement.go create mode 100644 v2/sarif/replacement_test.go create mode 100644 v2/sarif/reporting_configuration.go create mode 100644 v2/sarif/reporting_descriptor.go create mode 100644 v2/sarif/reporting_descriptor_reference.go create mode 100644 v2/sarif/reporting_descriptor_reference_test.go create mode 100644 v2/sarif/result.go create mode 100644 v2/sarif/result_provenance.go create mode 100644 v2/sarif/result_test.go create mode 100644 v2/sarif/run.go create mode 100644 v2/sarif/run_automation_details.go create mode 100644 v2/sarif/sarif.go create mode 100644 v2/sarif/special_locations.go create mode 100644 v2/sarif/stack.go create mode 100644 v2/sarif/stack_frame.go create mode 100644 v2/sarif/suppression.go create mode 100644 v2/sarif/test_helpers.go create mode 100644 v2/sarif/thread_flow.go create mode 100644 v2/sarif/thread_flow_location.go create mode 100644 v2/sarif/tool.go create mode 100644 v2/sarif/tool_component.go create mode 100644 v2/sarif/tool_component_reference.go create mode 100644 v2/sarif/translation_metadata.go create mode 100644 v2/sarif/version_control_details.go create mode 100644 v2/sarif/web_request.go create mode 100644 v2/sarif/web_response.go create mode 100644 v2/test/report_stage_test.go create mode 100644 v2/test/report_test.go create mode 100644 v2/test/run_stage_test.go create mode 100644 v2/test/run_test.go diff --git a/example/example-report.sarif b/example/example-report.sarif index 1561390..f380e48 100755 --- a/example/example-report.sarif +++ b/example/example-report.sarif @@ -13,6 +13,7 @@ "shortDescription": { "text": "Resource 'aws_security_group_rule.my-rule' defines a fully open ingress security group rule." }, + "helpUri": "See https://tfsec.dev/docs/aws/AWS006/ for more information.", "help": { "markdown": "# markdown" }, @@ -26,6 +27,7 @@ "shortDescription": { "text": "Resource 'azurerm_managed_disk.source' defines an unencrypted managed disk." }, + "helpUri": "See https://tfsec.dev/docs/azure/AZU003/ for more information.", "help": { "markdown": "# markdown" }, @@ -39,6 +41,7 @@ "shortDescription": { "text": "Resource 'aws_api_gateway_domain_name.outdated_security_policy' defines outdated SSL/TLS policies (not using TLS_1_2)." }, + "helpUri": "See https://tfsec.dev/docs/aws/AWS025/ for more information.", "help": { "markdown": "# markdown" }, @@ -52,6 +55,7 @@ "shortDescription": { "text": "Resource 'aws_security_group_rule.my-rule' should include a description for auditing purposes." }, + "helpUri": "See https://tfsec.dev/docs/aws/AWS018/ for more information.", "help": { "markdown": "# markdown" }, @@ -65,6 +69,7 @@ "shortDescription": { "text": "Resource 'aws_alb_listener.my-alb-listener' uses plain HTTP instead of HTTPS." }, + "helpUri": "See https://tfsec.dev/docs/aws/AWS004/ for more information.", "help": { "markdown": "# markdown" }, @@ -78,6 +83,7 @@ "shortDescription": { "text": "Resource 'aws_db_security_group.my-group' uses EC2 Classic. Use a VPC instead." }, + "helpUri": "See https://tfsec.dev/docs/aws/AWS003/ for more information.", "help": { "markdown": "# markdown" }, @@ -91,6 +97,7 @@ "shortDescription": { "text": "Resource 'aws_dynamodb_table.bad_example' is not using KMS CMK for encryption" }, + "helpUri": "See https://tfsec.dev/docs/aws/AWS092/ for more information.", "help": { "markdown": "# markdown" }, @@ -294,28 +301,4 @@ ] } ] -}in.tf" - }, - "region": { - "startLine": 41, - "endLine": 56 - } - } - } - ] - } - ] - } - ] -} - "startLine": 41, - "endLine": 56 - } - } - } - ] - } - ] - } - ] } \ No newline at end of file diff --git a/example/main.go b/example/main.go index be3b92e..c853894 100644 --- a/example/main.go +++ b/example/main.go @@ -6,10 +6,10 @@ import ( "os" "strings" - "github.com/owenrumney/go-sarif/sarif" + "github.com/owenrumney/go-sarif/v2/sarif" ) -// simple structure for the output of tfsec +// TfsecResults is a simple structure for the output of tfsec type TfsecResults struct { Results []struct { RuleID string `json:"rule_id"` @@ -44,7 +44,7 @@ func main() { } // create a run for tfsec - run := sarif.NewRun("tfsec", "https://tfsec.dev") + run := sarif.NewRunWithInformationURI("tfsec", "https://tfsec.dev") // for each result, add the for _, r := range tfsecResults.Results { @@ -57,7 +57,7 @@ func main() { // create a new rule for each rule id run.AddRule(r.RuleID). WithDescription(r.Description). - WithHelp(r.Link). + WithHelpURI(r.Link). WithProperties(pb.Properties). WithMarkdownHelp("# markdown") @@ -65,10 +65,10 @@ func main() { run.AddDistinctArtifact(r.Location.Filename) // add each of the results with the details of where the issue occurred - run.AddResult(r.RuleID). + run.CreateResultForRule(r.RuleID). WithLevel(strings.ToLower(r.Severity)). WithMessage(sarif.NewTextMessage(r.Description)). - WithLocation( + AddLocation( sarif.NewLocationWithPhysicalLocation( sarif.NewPhysicalLocation(). WithArtifactLocation( diff --git a/go.mod b/go.mod index 515ad97..3ce769a 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,9 @@ module github.com/owenrumney/go-sarif go 1.16 require ( + github.com/owenrumney/go-sarif/v2 v2.0.0 github.com/stretchr/testify v1.7.0 github.com/zclconf/go-cty v1.10.0 ) + +replace github.com/owenrumney/go-sarif/v2 v2.0.0 => ./v2 diff --git a/go.sum b/go.sum index 10c2d13..f2005f5 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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= diff --git a/v2/go.mod b/v2/go.mod new file mode 100644 index 0000000..12ff524 --- /dev/null +++ b/v2/go.mod @@ -0,0 +1,9 @@ +module github.com/owenrumney/go-sarif/v2 + +go 1.16 + +require ( + github.com/owenrumney/go-sarif v1.1.1 + github.com/stretchr/testify v1.7.0 + github.com/zclconf/go-cty v1.10.0 +) diff --git a/v2/go.sum b/v2/go.sum new file mode 100644 index 0000000..dbac08c --- /dev/null +++ b/v2/go.sum @@ -0,0 +1,39 @@ +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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +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 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/owenrumney/go-sarif v1.1.1 h1:QNObu6YX1igyFKhdzd7vgzmw7XsWN3/6NMGuDzBgXmE= +github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +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.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= +github.com/zclconf/go-cty v1.10.0/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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/sarif/address.go b/v2/sarif/address.go new file mode 100644 index 0000000..cdedcf2 --- /dev/null +++ b/v2/sarif/address.go @@ -0,0 +1,77 @@ +package sarif + +// Address ... +type Address struct { + PropertyBag + Index *uint `json:"index,omitempty"` + AbsoluteAddress *uint `json:"absoluteAddress,omitempty"` + RelativeAddress *int `json:"relativeAddress,omitempty"` + OffsetFromParent *int `json:"offsetFromParent,omitempty"` + Length *int `json:"length,omitempty"` + Name *string `json:"name,omitempty"` + FullyQualifiedName *string `json:"fullyQualifiedName,omitempty"` + Kind *string `json:"kind,omitempty"` + ParentIndex *uint `json:"parentIndex,omitempty"` +} + +// NewAddress create a new Address and returns a pointer to it +func NewAddress() *Address { + return &Address{} +} + +// WithIndex sets the Index +func (address *Address) WithIndex(index int) *Address { + i := uint(index) + address.Index = &i + return address +} + +// WithAbsoluteAddress sets the AbsoluteAddress +func (address *Address) WithAbsoluteAddress(absoluteAddress int) *Address { + i := uint(absoluteAddress) + address.AbsoluteAddress = &i + return address +} + +// WithRelativeAddress sets the RelativeAddress +func (address *Address) WithRelativeAddress(relativeAddress int) *Address { + address.RelativeAddress = &relativeAddress + return address +} + +// WithOffsetFromParent sets the OffsetFromParent +func (address *Address) WithOffsetFromParent(offsetFromParent int) *Address { + address.OffsetFromParent = &offsetFromParent + return address +} + +// WithLength sets the Length +func (address *Address) WithLength(length int) *Address { + address.Length = &length + return address +} + +// WithName sets the Name +func (address *Address) WithName(name string) *Address { + address.Name = &name + return address +} + +// WithFullyQualifiedName sets the FullyQualifiedName +func (address *Address) WithFullyQualifiedName(fullyQualifiedName string) *Address { + address.FullyQualifiedName = &fullyQualifiedName + return address +} + +// WithKind sets the Kind +func (address *Address) WithKind(kind string) *Address { + address.Kind = &kind + return address +} + +// WithParentIndex sets the ParentIndex +func (address *Address) WithParentIndex(parentIndex int) *Address { + i := uint(parentIndex) + address.ParentIndex = &i + return address +} diff --git a/v2/sarif/address_test.go b/v2/sarif/address_test.go new file mode 100644 index 0000000..2e4efde --- /dev/null +++ b/v2/sarif/address_test.go @@ -0,0 +1,29 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_new_simple_address(t *testing.T) { + + address := NewAddress(). + WithIndex(1). + WithName("google"). + WithFullyQualifiedName("https://www.google.com") + + assert.Equal(t, `{"index":1,"name":"google","fullyQualifiedName":"https://www.google.com"}`, getJsonString(address)) +} + +func Test_create_new_absolute_address(t *testing.T) { + + address := NewAddress(). + WithIndex(1). + WithName("google"). + WithAbsoluteAddress(1). + WithKind("url"). + WithLength(10) + + assert.Equal(t, `{"index":1,"absoluteAddress":1,"length":10,"name":"google","kind":"url"}`, getJsonString(address)) +} diff --git a/v2/sarif/artifact.go b/v2/sarif/artifact.go new file mode 100644 index 0000000..0ba661d --- /dev/null +++ b/v2/sarif/artifact.go @@ -0,0 +1,115 @@ +package sarif + +// Artifact ... +type Artifact struct { + PropertyBag + Location *ArtifactLocation `json:"location,omitempty"` + ParentIndex *uint `json:"parentIndex,omitempty"` + Offset *uint `json:"offset,omitempty"` + Length int `json:"length"` + Roles []string `json:"roles,omitempty"` + MimeType *string `json:"mimeType,omitempty"` + Contents *ArtifactContent `json:"contents,omitempty"` + Encoding *string `json:"encoding,omitempty"` + SourceLanguage *string `json:"sourceLanguage,omitempty"` + Hashes map[string]string `json:"hashes,omitempty"` + LastModifiedTimeUtc *string `json:"lastModifiedTimeUtc,omitempty"` + Description *Message `json:"description,omitempty"` +} + +// NewArtifact creates a new Artifact and returns a pointer to it +func NewArtifact() *Artifact { + return &Artifact{} +} + +// WithLocation sets the Location +func (artifact *Artifact) WithLocation(artifactLocation *ArtifactLocation) *Artifact { + artifact.Location = artifactLocation + return artifact +} + +// WithParentIndex sets the ParentIndex +func (artifact *Artifact) WithParentIndex(parentIndex int) *Artifact { + i := uint(parentIndex) + artifact.ParentIndex = &i + return artifact +} + +// WithOffset sets the Offset +func (artifact *Artifact) WithOffset(offset int) *Artifact { + o := uint(offset) + artifact.Offset = &o + return artifact +} + +// WithLength sets the Length +func (artifact *Artifact) WithLength(length int) *Artifact { + artifact.Length = length + return artifact +} + +// WithRole sets the Role +func (artifact *Artifact) WithRole(role string) *Artifact { + artifact.Roles = append(artifact.Roles, role) + return artifact +} + +// WithMimeType sets the MimeType +func (artifact *Artifact) WithMimeType(mimeType string) *Artifact { + artifact.MimeType = &mimeType + return artifact +} + +// WithContents sets the Contents +func (artifact *Artifact) WithContents(artifactContent *ArtifactContent) *Artifact { + artifact.Contents = artifactContent + return artifact +} + +// WithEncoding sets the Encoding +func (artifact *Artifact) WithEncoding(encoding string) *Artifact { + artifact.Encoding = &encoding + return artifact +} + +// WithSourceLanguage sets the SourceLanguage +func (artifact *Artifact) WithSourceLanguage(sourceLanguage string) *Artifact { + artifact.SourceLanguage = &sourceLanguage + return artifact +} + +// WithHashes sets the Hashes +func (artifact *Artifact) WithHashes(hashes map[string]string) *Artifact { + artifact.Hashes = hashes + return artifact +} + +// WithLastModifiedTimeUtc sets the LastModifiedTimeUtc +func (artifact *Artifact) WithLastModifiedTimeUtc(lastModified string) *Artifact { + artifact.LastModifiedTimeUtc = &lastModified + return artifact +} + +// WithDescription sets the Description +func (artifact *Artifact) WithDescription(message *Message) *Artifact { + artifact.Description = message + return artifact +} + +// WithDescriptionText sets the DescriptionText +func (artifact *Artifact) WithDescriptionText(text string) *Artifact { + if artifact.Description == nil { + artifact.Description = &Message{} + } + artifact.Description.Text = &text + return artifact +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (artifact *Artifact) WithDescriptionMarkdown(markdown string) *Artifact { + if artifact.Description == nil { + artifact.Description = &Message{} + } + artifact.Description.Markdown = &markdown + return artifact +} diff --git a/v2/sarif/artifact_change.go b/v2/sarif/artifact_change.go new file mode 100644 index 0000000..3446103 --- /dev/null +++ b/v2/sarif/artifact_change.go @@ -0,0 +1,21 @@ +package sarif + +// ArtifactChange ... +type ArtifactChange struct { + PropertyBag + ArtifactLocation ArtifactLocation `json:"artifactLocation"` + Replacements []*Replacement `json:"replacements"` +} + +// NewArtifactChange creates a new ArtifactChange and returns a pointer to it +func NewArtifactChange(artifactLocation *ArtifactLocation) *ArtifactChange { + return &ArtifactChange{ + ArtifactLocation: *artifactLocation, + } +} + +// WithReplacement sets the Replacement +func (artifactChange *ArtifactChange) WithReplacement(replacement *Replacement) *ArtifactChange { + artifactChange.Replacements = append(artifactChange.Replacements, replacement) + return artifactChange +} diff --git a/v2/sarif/artifact_change_test.go b/v2/sarif/artifact_change_test.go new file mode 100644 index 0000000..803130f --- /dev/null +++ b/v2/sarif/artifact_change_test.go @@ -0,0 +1,26 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_simple_artifact_change(t *testing.T) { + + ac := NewArtifactChange(NewArtifactLocation(). + WithIndex(0). + WithUri("file://broken.go"). + WithDescription(NewMessage().WithText("message text"))) + + ac.WithReplacement( + NewReplacement( + NewRegion(). + WithSnippet(NewArtifactContent().WithText("file://broken.go")). + WithStartLine(1). + WithEndLine(10), + ), + ) + + assert.Equal(t, `{"artifactLocation":{"uri":"file://broken.go","index":0,"description":{"text":"message text"}},"replacements":[{"deletedRegion":{"startLine":1,"endLine":10,"snippet":{"text":"file://broken.go"}}}]}`, getJsonString(ac)) +} diff --git a/v2/sarif/artifact_content.go b/v2/sarif/artifact_content.go new file mode 100644 index 0000000..6abb305 --- /dev/null +++ b/v2/sarif/artifact_content.go @@ -0,0 +1,32 @@ +package sarif + +// ArtifactContent ... +type ArtifactContent struct { + PropertyBag + Text *string `json:"text,omitempty"` + Binary *string `json:"binary,omitempty"` + Rendered *MultiformatMessageString `json:"rendered,omitempty"` +} + +// NewArtifactContent creates a new ArtifactContent and returns a pointer to it +func NewArtifactContent() *ArtifactContent { + return &ArtifactContent{} +} + +// WithText sets the Text +func (artifactContent *ArtifactContent) WithText(text string) *ArtifactContent { + artifactContent.Text = &text + return artifactContent +} + +// WithBinary sets the Binary +func (artifactContent *ArtifactContent) WithBinary(binary string) *ArtifactContent { + artifactContent.Binary = &binary + return artifactContent +} + +// WithRendered sets the Rendered +func (artifactContent *ArtifactContent) WithRendered(mms *MultiformatMessageString) *ArtifactContent { + artifactContent.Rendered = mms + return artifactContent +} diff --git a/v2/sarif/artifact_content_test.go b/v2/sarif/artifact_content_test.go new file mode 100644 index 0000000..d80bbf1 --- /dev/null +++ b/v2/sarif/artifact_content_test.go @@ -0,0 +1,16 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_artifact_content(t *testing.T) { + ac := NewArtifactContent() + ac.WithText("artifact body"). + WithBinary("broken.exe"). + WithRendered(NewMultiformatMessageString("mms string content")) + + assert.Equal(t, `{"text":"artifact body","binary":"broken.exe","rendered":{"text":"mms string content"}}`, getJsonString(ac)) +} diff --git a/v2/sarif/artifact_location.go b/v2/sarif/artifact_location.go new file mode 100644 index 0000000..e8502ff --- /dev/null +++ b/v2/sarif/artifact_location.go @@ -0,0 +1,63 @@ +package sarif + +// ArtifactLocation ... +type ArtifactLocation struct { + PropertyBag + URI *string `json:"uri,omitempty"` + URIBaseId *string `json:"uriBaseId,omitempty"` + Index *uint `json:"index,omitempty"` + Description *Message `json:"description,omitempty"` +} + +// NewArtifactLocation creates a new ArtifactLocation and returns a pointer to it +func NewArtifactLocation() *ArtifactLocation { + return &ArtifactLocation{} +} + +// NewSimpleArtifactLocation creates a new SimpleArtifactLocation and returns a pointer to it +func NewSimpleArtifactLocation(uri string) *ArtifactLocation { + return NewArtifactLocation().WithUri(uri) +} + +// WithUri sets the Uri +func (artifactLocation *ArtifactLocation) WithUri(uri string) *ArtifactLocation { + artifactLocation.URI = &uri + return artifactLocation +} + +// WithUriBaseId sets the UriBaseId +func (artifactLocation *ArtifactLocation) WithUriBaseId(uriBaseId string) *ArtifactLocation { + artifactLocation.URIBaseId = &uriBaseId + return artifactLocation +} + +// WithIndex sets the Index +func (artifactLocation *ArtifactLocation) WithIndex(index int) *ArtifactLocation { + i := uint(index) + artifactLocation.Index = &i + return artifactLocation +} + +// WithDescription sets the Description +func (artifactLocation *ArtifactLocation) WithDescription(message *Message) *ArtifactLocation { + artifactLocation.Description = message + return artifactLocation +} + +// WithDescriptionText sets the DescriptionText +func (artifactLocation *ArtifactLocation) WithDescriptionText(text string) *ArtifactLocation { + if artifactLocation.Description == nil { + artifactLocation.Description = &Message{} + } + artifactLocation.Description.Text = &text + return artifactLocation +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (artifactLocation *ArtifactLocation) WithDescriptionMarkdown(markdown string) *ArtifactLocation { + if artifactLocation.Description == nil { + artifactLocation.Description = &Message{} + } + artifactLocation.Description.Markdown = &markdown + return artifactLocation +} diff --git a/v2/sarif/artifact_location_test.go b/v2/sarif/artifact_location_test.go new file mode 100644 index 0000000..986c1cf --- /dev/null +++ b/v2/sarif/artifact_location_test.go @@ -0,0 +1,17 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_new_artifact_location(t *testing.T) { + al := NewArtifactLocation(). + WithIndex(0). + WithUri("file://broken.go"). + WithUriBaseId("baseId"). + WithDescription(NewMessage().WithText("message text")) + + assert.Equal(t, `{"uri":"file://broken.go","uriBaseId":"baseId","index":0,"description":{"text":"message text"}}`, getJsonString(al)) +} diff --git a/v2/sarif/artifact_test.go b/v2/sarif/artifact_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/artifact_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/attachment.go b/v2/sarif/attachment.go new file mode 100644 index 0000000..b5567ea --- /dev/null +++ b/v2/sarif/attachment.go @@ -0,0 +1,55 @@ +package sarif + +// Attachment ... +type Attachment struct { + PropertyBag + ArtifactLocation *ArtifactLocation `json:"artifactLocation,omitempty"` + Description *Message `json:"description,omitempty"` + Rectangles []*Rectangle `json:"rectangles,omitempty"` +} + +// NewAttachment creates a new Attachment and returns a pointer to it +func NewAttachment() *Attachment { + return &Attachment{} +} + +// WithArtifactionLocation sets the ArtifactionLocation +func (attachment *Attachment) WithArtifactionLocation(artifactLocation *ArtifactLocation) *Attachment { + attachment.ArtifactLocation = artifactLocation + return attachment +} + +// WithDescription sets the Description +func (attachment *Attachment) WithDescription(description *Message) *Attachment { + attachment.Description = description + return attachment +} + +// WithDescriptionText sets the DescriptionText +func (attachment *Attachment) WithDescriptionText(text string) *Attachment { + if attachment.Description == nil { + attachment.Description = &Message{} + } + attachment.Description.Text = &text + return attachment +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (attachment *Attachment) WithDescriptionMarkdown(markdown string) *Attachment { + if attachment.Description == nil { + attachment.Description = &Message{} + } + attachment.Description.Markdown = &markdown + return attachment +} + +// WithRectangles sets the Rectangles +func (attachment *Attachment) WithRectangles(rectangles []*Rectangle) *Attachment { + attachment.Rectangles = rectangles + return attachment +} + +// AddRectangle ... +func (attachment *Attachment) AddRectangle(rectangle *Rectangle) { + attachment.Rectangles = append(attachment.Rectangles, rectangle) +} diff --git a/v2/sarif/code_flow.go b/v2/sarif/code_flow.go new file mode 100644 index 0000000..aaf23d8 --- /dev/null +++ b/v2/sarif/code_flow.go @@ -0,0 +1,49 @@ +package sarif + +// CodeFlow ... +type CodeFlow struct { + PropertyBag + Message *Message `json:"message,omitempty"` + ThreadFlows []*ThreadFlow `json:"threadFlows,omitempty"` +} + +// NewCodeFlow creates a new CodeFlow and returns a pointer to it +func NewCodeFlow() *CodeFlow { + return &CodeFlow{} +} + +// WithMessage sets the Message +func (codeFlow *CodeFlow) WithMessage(message *Message) *CodeFlow { + codeFlow.Message = message + return codeFlow +} + +// WithTextMessage sets the Message text +func (codeFlow *CodeFlow) WithTextMessage(text string) *CodeFlow { + if codeFlow.Message == nil { + codeFlow.Message = &Message{} + } + codeFlow.Message.Text = &text + return codeFlow +} + +// WithMessageMarkdown sets the Message markdown +func (codeFlow *CodeFlow) WithMessageMarkdown(markdown string) *CodeFlow { + if codeFlow.Message == nil { + codeFlow.Message = &Message{} + } + codeFlow.Message.Markdown = &markdown + return codeFlow +} + +// WithThreadFlows sets the ThreadFlows +func (codeFlow *CodeFlow) WithThreadFlows(threadFlows []*ThreadFlow) *CodeFlow { + codeFlow.ThreadFlows = threadFlows + + return codeFlow +} + +// AddThreadFlow ... +func (codeFlow *CodeFlow) AddThreadFlow(threadFlow *ThreadFlow) { + codeFlow.ThreadFlows = append(codeFlow.ThreadFlows, threadFlow) +} diff --git a/v2/sarif/configuration_override.go b/v2/sarif/configuration_override.go new file mode 100644 index 0000000..32a7cbb --- /dev/null +++ b/v2/sarif/configuration_override.go @@ -0,0 +1,25 @@ +package sarif + +// ConfigurationOverride ... +type ConfigurationOverride struct { + PropertyBag + Configuration *ReportingConfiguration `json:"configuration,omitempty"` + Descriptor *ReportingDescriptorReference `json:"descriptor,omitempty"` +} + +// NewConfigurationOverride creates a new ConfigurationOverride and returns a pointer to it +func NewConfigurationOverride() *ConfigurationOverride { + return &ConfigurationOverride{} +} + +// WithDescriptor sets the Descriptor +func (configurationOverride *ConfigurationOverride) WithDescriptor(descriptor *ReportingDescriptorReference) *ConfigurationOverride { + configurationOverride.Descriptor = descriptor + return configurationOverride +} + +// WithConfiguration sets the Configuration +func (configurationOverride *ConfigurationOverride) WithConfiguration(configuration *ReportingConfiguration) *ConfigurationOverride { + configurationOverride.Configuration = configuration + return configurationOverride +} diff --git a/v2/sarif/conversion.go b/v2/sarif/conversion.go new file mode 100644 index 0000000..857382b --- /dev/null +++ b/v2/sarif/conversion.go @@ -0,0 +1,26 @@ +package sarif + +// Conversion ... +type Conversion struct { + PropertyBag + AnalysisToolLogFiles []*ArtifactLocation `json:"analysisToolLogFiles,omitempty"` + Invocation *Invocation `json:"invocation,omitempty"` + Tool *Tool `json:"tool"` +} + +// NewConversion creates a new Conversion and returns a pointer to it +func NewConversion() *Conversion { + return &Conversion{} +} + +// WithInvocation sets the Invocation +func (conversion *Conversion) WithInvocation(invocation *Invocation) *Conversion { + conversion.Invocation = invocation + return conversion +} + +// WithTool sets the Tool +func (conversion *Conversion) WithTool(tool *Tool) *Conversion { + conversion.Tool = tool + return conversion +} diff --git a/v2/sarif/edge.go b/v2/sarif/edge.go new file mode 100644 index 0000000..63b1d2c --- /dev/null +++ b/v2/sarif/edge.go @@ -0,0 +1,47 @@ +package sarif + +// Edge ... +type Edge struct { + PropertyBag + ID string `json:"id"` + Label *Message `json:"label,omitempty"` + SourceNodeID string `json:"sourceNodeId"` + TargetNodeID string `json:"targetNodeId"` +} + +// NewEdge creates a new Edge and returns a pointer to it +func NewEdge(id, sourceNodeID, targetNodeID string) *Edge { + return &Edge{ + ID: id, + SourceNodeID: sourceNodeID, + TargetNodeID: targetNodeID, + } +} + +// WithID sets the ID +func (edge *Edge) WithID(id string) *Edge { + edge.ID = id + return edge +} + +// WithLabel sets the Label +func (edge *Edge) WithLabel(label *Message) *Edge { + edge.Label = label + return edge +} + +// WithLabelText sets the LabelText +func (edge *Edge) WithLabelText(text string) *Edge { + edge.Label = &Message{ + Text: &text, + } + return edge +} + +// WithLabelMarkdown sets the LabelMarkdown +func (edge *Edge) WithLabelMarkdown(markdown string) *Edge { + edge.Label = &Message{ + Markdown: &markdown, + } + return edge +} diff --git a/v2/sarif/edge_traversal.go b/v2/sarif/edge_traversal.go new file mode 100644 index 0000000..02a7ec0 --- /dev/null +++ b/v2/sarif/edge_traversal.go @@ -0,0 +1,56 @@ +package sarif + +// EdgeTraversal ... +type EdgeTraversal struct { + PropertyBag + EdgeID string `json:"edgeId"` + FinalState map[string]*MultiformatMessageString `json:"finalState,omitempty"` + Message *Message `json:"message,omitempty"` + StepOverEdgeCount *int `json:"stepOverEdgeCount,omitempty"` +} + +// NewEdgeTraversal creates a new EdgeTraversal and returns a pointer to it +func NewEdgeTraversal(edgeID string) *EdgeTraversal { + return &EdgeTraversal{ + EdgeID: edgeID, + } +} + +// WithDescription sets the Description +func (edgeTraversal *EdgeTraversal) WithDescription(message *Message) *EdgeTraversal { + edgeTraversal.Message = message + return edgeTraversal +} + +// WithDescriptionText sets the DescriptionText +func (edgeTraversal *EdgeTraversal) WithDescriptionText(text string) *EdgeTraversal { + edgeTraversal.Message = &Message{ + Text: &text, + } + return edgeTraversal +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (edgeTraversal *EdgeTraversal) WithDescriptionMarkdown(markdown string) *EdgeTraversal { + edgeTraversal.Message = &Message{ + Markdown: &markdown, + } + return edgeTraversal +} + +// WithFinalState sets the FinalState +func (edgeTraversal *EdgeTraversal) WithFinalState(finalState map[string]*MultiformatMessageString) *EdgeTraversal { + edgeTraversal.FinalState = finalState + return edgeTraversal +} + +// SetFinalState ... +func (edgeTraversal *EdgeTraversal) SetFinalState(key string, state *MultiformatMessageString) { + edgeTraversal.FinalState[key] = state +} + +// WithStepOverEdgeCount sets the StepOverEdgeCount +func (edgeTraversal *EdgeTraversal) WithStepOverEdgeCount(stepOverEdgeCount int) *EdgeTraversal { + edgeTraversal.StepOverEdgeCount = &stepOverEdgeCount + return edgeTraversal +} diff --git a/v2/sarif/exception.go b/v2/sarif/exception.go new file mode 100644 index 0000000..89d3b4d --- /dev/null +++ b/v2/sarif/exception.go @@ -0,0 +1,44 @@ +package sarif + +// Exception ... +type Exception struct { + PropertyBag + InnerExceptions []*Exception `json:"innerExceptions,omitempty"` + Kind *string `json:"kind,omitempty"` + Message *string `json:"message,omitempty"` + Stack *Stack `json:"stack,omitempty"` +} + +// NewException creates a new Exception and returns a pointer to it +func NewException() *Exception { + return &Exception{} +} + +// WithMessage sets the Message +func (exception *Exception) WithMessage(message string) *Exception { + exception.Message = &message + return exception +} + +// WithKind sets the Kind +func (exception *Exception) WithKind(kind string) *Exception { + exception.Kind = &kind + return exception +} + +// WithStack sets the Stack +func (exception *Exception) WithStack(stack Stack) *Exception { + exception.Stack = &stack + return exception +} + +// WithInnerExceptions sets the InnerExceptions +func (exception *Exception) WithInnerExceptions(exceptions []*Exception) *Exception { + exception.InnerExceptions = exceptions + return exception +} + +// AddInnerException ... +func (exception *Exception) AddInnerException(toAdd *Exception) { + exception.InnerExceptions = append(exception.InnerExceptions, toAdd) +} diff --git a/v2/sarif/external_properties.go b/v2/sarif/external_properties.go new file mode 100644 index 0000000..654e59c --- /dev/null +++ b/v2/sarif/external_properties.go @@ -0,0 +1,216 @@ +package sarif + +// ExternalProperties ... +type ExternalProperties struct { + PropertyBag + Addresses []*Address `json:"addresses,omitempty"` + Artifacts []*Artifact `json:"artifacts,omitempty"` + Conversion *Conversion `json:"conversion,omitempty"` + Driver *ToolComponent `json:"driver,omitempty"` + Extensions []*ToolComponent `json:"extensions,omitempty"` + ExternalizedProperties *PropertyBag `json:"externalizedProperties,omitempty"` + Graphs []*Graph `json:"graphs,omitempty"` + GUID *string `json:"guid,omitempty"` + Invocations []*Invocation `json:"invocations,omitempty"` + LogicalLocations []*LogicalLocation `json:"logicalLocations,omitempty"` + Policies []*ToolComponent `json:"policies,omitempty"` + Results []*Result `json:"results,omitempty"` + RunGUID *string `json:"runGuid,omitempty"` + Schema *string `json:"schema,omitempty"` + Taxonomies []*ToolComponent `json:"taxonomies,omitempty"` + ThreadFlowLocations []*ThreadFlowLocation `json:"threadFlowLocations,omitempty"` + Translations []*ToolComponent `json:"translations,omitempty"` + Version string `json:"version,omitempty"` + WebRequests []*WebRequest `json:"webRequests,omitempty"` + WebResponses []*WebResponse `json:"webResponses,omitempty"` +} + +// NewExternalProperties creates a new ExternalProperties and returns a pointer to it +func NewExternalProperties() *ExternalProperties { + return &ExternalProperties{} +} + +// WithAddress sets the Address +func (externalProperties *ExternalProperties) WithAddress(addresses []*Address) *ExternalProperties { + externalProperties.Addresses = addresses + return externalProperties +} + +// AddAddress ... +func (externalProperties *ExternalProperties) AddAddress(address *Address) { + externalProperties.Addresses = append(externalProperties.Addresses, address) +} + +// WithArtifact sets the Artifact +func (externalProperties *ExternalProperties) WithArtifact(artifacts []*Artifact) *ExternalProperties { + externalProperties.Artifacts = artifacts + return externalProperties +} + +// AddArtifact ... +func (externalProperties *ExternalProperties) AddArtifact(artifact *Artifact) { + externalProperties.Artifacts = append(externalProperties.Artifacts, artifact) +} + +// WithConversion sets the Conversion +func (externalProperties *ExternalProperties) WithConversion(conversion *Conversion) *ExternalProperties { + externalProperties.Conversion = conversion + return externalProperties +} + +// WithDriver sets the Driver +func (externalProperties *ExternalProperties) WithDriver(driver *ToolComponent) *ExternalProperties { + externalProperties.Driver = driver + return externalProperties +} + +// WithExtensions sets the Extensions +func (externalProperties *ExternalProperties) WithExtensions(extensions []*ToolComponent) *ExternalProperties { + externalProperties.Extensions = extensions + return externalProperties +} + +// AddExtension ... +func (externalProperties *ExternalProperties) AddExtension(extension *ToolComponent) { + externalProperties.Extensions = append(externalProperties.Extensions, extension) +} + +// WithExternalizedProperties sets the ExternalizedProperties +func (externalProperties *ExternalProperties) WithExternalizedProperties(externalizedProperties *PropertyBag) *ExternalProperties { + externalProperties.ExternalizedProperties = externalizedProperties + return externalProperties +} + +// WithGraphs sets the Graphs +func (externalProperties *ExternalProperties) WithGraphs(graphs []*Graph) *ExternalProperties { + externalProperties.Graphs = graphs + return externalProperties +} + +// AddGraph ... +func (externalProperties *ExternalProperties) AddGraph(graph *Graph) { + externalProperties.Graphs = append(externalProperties.Graphs, graph) +} + +// WithGUID sets the GUID +func (externalProperties *ExternalProperties) WithGUID(guid string) *ExternalProperties { + externalProperties.GUID = &guid + return externalProperties +} + +// WithInvocations sets the Invocations +func (externalProperties *ExternalProperties) WithInvocations(invocations []*Invocation) *ExternalProperties { + externalProperties.Invocations = invocations + return externalProperties +} + +// AddInvocation ... +func (externalProperties *ExternalProperties) AddInvocation(invocation *Invocation) { + externalProperties.Invocations = append(externalProperties.Invocations, invocation) +} + +// WithLogicalLocations sets the LogicalLocations +func (externalProperties *ExternalProperties) WithLogicalLocations(logicalLocations []*LogicalLocation) *ExternalProperties { + externalProperties.LogicalLocations = logicalLocations + return externalProperties +} + +// AddLogicalLocation ... +func (externalProperties *ExternalProperties) AddLogicalLocation(logicalLocation *LogicalLocation) { + externalProperties.LogicalLocations = append(externalProperties.LogicalLocations, logicalLocation) +} + +// WithPolicies sets the Policies +func (externalProperties *ExternalProperties) WithPolicies(policies []*ToolComponent) *ExternalProperties { + externalProperties.Policies = policies + return externalProperties +} + +// AddPolicy ... +func (externalProperties *ExternalProperties) AddPolicy(policy *ToolComponent) { + externalProperties.Policies = append(externalProperties.Policies, policy) +} + +// WithResults sets the Results +func (externalProperties *ExternalProperties) WithResults(results []*Result) *ExternalProperties { + externalProperties.Results = results + return externalProperties +} + +// AddResult ... +func (externalProperties *ExternalProperties) AddResult(result *Result) { + externalProperties.Results = append(externalProperties.Results, result) +} + +// WithRunGUID sets the RunGUID +func (externalProperties *ExternalProperties) WithRunGUID(runGUID string) *ExternalProperties { + externalProperties.RunGUID = &runGUID + return externalProperties +} + +// WithSchema sets the Schema +func (externalProperties *ExternalProperties) WithSchema(schema string) *ExternalProperties { + externalProperties.Schema = &schema + return externalProperties +} + +// WithVersion sets the Version +func (externalProperties *ExternalProperties) WithVersion(version string) *ExternalProperties { + externalProperties.Version = version + return externalProperties +} + +// WithTaxonomies sets the Taxonomies +func (externalProperties *ExternalProperties) WithTaxonomies(taxonomies []*ToolComponent) *ExternalProperties { + externalProperties.Taxonomies = taxonomies + return externalProperties +} + +// AddTaxonomie ... +func (externalProperties *ExternalProperties) AddTaxonomie(taxonomy *ToolComponent) { + externalProperties.Taxonomies = append(externalProperties.Taxonomies, taxonomy) +} + +// WithThreadFlowLocations sets the ThreadFlowLocations +func (externalProperties *ExternalProperties) WithThreadFlowLocations(threadFlowLocations []*ThreadFlowLocation) *ExternalProperties { + externalProperties.ThreadFlowLocations = threadFlowLocations + return externalProperties +} + +// AddThreadFlowLocations ... +func (externalProperties *ExternalProperties) AddThreadFlowLocations(threadFlowLocation *ThreadFlowLocation) { + externalProperties.ThreadFlowLocations = append(externalProperties.ThreadFlowLocations, threadFlowLocation) +} + +// WithTranslations sets the Translations +func (externalProperties *ExternalProperties) WithTranslations(translation []*ToolComponent) *ExternalProperties { + externalProperties.Translations = translation + return externalProperties +} + +// AddTranslation ... +func (externalProperties *ExternalProperties) AddTranslation(translation *ToolComponent) { + externalProperties.Translations = append(externalProperties.Translations, translation) +} + +// WithWebRequests sets the WebRequests +func (externalProperties *ExternalProperties) WithWebRequests(webRequests []*WebRequest) *ExternalProperties { + externalProperties.WebRequests = webRequests + return externalProperties +} + +// AddWebRequest ... +func (externalProperties *ExternalProperties) AddWebRequest(webRequest *WebRequest) { + externalProperties.WebRequests = append(externalProperties.WebRequests, webRequest) +} + +// WithWebResponses sets the WebResponses +func (externalProperties *ExternalProperties) WithWebResponses(webResponses []*WebResponse) *ExternalProperties { + externalProperties.WebResponses = webResponses + return externalProperties +} + +// AddWebResponse ... +func (externalProperties *ExternalProperties) AddWebResponse(webResponse *WebResponse) { + externalProperties.WebResponses = append(externalProperties.WebResponses, webResponse) +} diff --git a/v2/sarif/external_property_file_reference.go b/v2/sarif/external_property_file_reference.go new file mode 100644 index 0000000..cfb73f8 --- /dev/null +++ b/v2/sarif/external_property_file_reference.go @@ -0,0 +1,26 @@ +package sarif + +// ExternalPropertyFileReference ... +type ExternalPropertyFileReference struct { + PropertyBag + GUID *string `json:"guid,omitempty"` + ItemCount *int `json:"itemCount,omitempty"` + Location *ArtifactLocation `json:"location,omitempty"` +} + +// NewExternalPropertyFileReference creates a new ExternalPropertyFileReference and returns a pointer to it +func NewExternalPropertyFileReference() *ExternalPropertyFileReference { + return &ExternalPropertyFileReference{} +} + +// WithGUID sets the GUID +func (externalPropertyFileReferences *ExternalPropertyFileReference) WithGUID(guid string) *ExternalPropertyFileReference { + externalPropertyFileReferences.GUID = &guid + return externalPropertyFileReferences +} + +// WithItemCount sets the ItemCount +func (externalPropertyFileReferences *ExternalPropertyFileReference) WithItemCount(itemCount int) *ExternalPropertyFileReference { + externalPropertyFileReferences.ItemCount = &itemCount + return externalPropertyFileReferences +} diff --git a/v2/sarif/external_property_file_references.go b/v2/sarif/external_property_file_references.go new file mode 100644 index 0000000..ee3f8a0 --- /dev/null +++ b/v2/sarif/external_property_file_references.go @@ -0,0 +1,189 @@ +package sarif + +// ExternalPropertyFileReferences ... +type ExternalPropertyFileReferences struct { + PropertyBag + Addresses []*ExternalPropertyFileReference `json:"addresses,omitempty"` + Artifacts []*ExternalPropertyFileReference `json:"artifacts,omitempty"` + Conversion *ExternalPropertyFileReference `json:"conversion,omitempty"` + Driver *ExternalPropertyFileReference `json:"driver,omitempty"` + Extensions []*ExternalPropertyFileReference `json:"extensions,omitempty"` + ExternalizedProperties *ExternalPropertyFileReference `json:"externalizedProperties,omitempty"` + Graphs []*ExternalPropertyFileReference `json:"graphs,omitempty"` + Invocations []*ExternalPropertyFileReference `json:"invocations,omitempty"` + LogicalLocations []*ExternalPropertyFileReference `json:"logicalLocations,omitempty"` + Policies []*ExternalPropertyFileReference `json:"policies,omitempty"` + Properties *PropertyBag `json:"properties,omitempty"` + Results []*ExternalPropertyFileReference `json:"results,omitempty"` + Taxonomies []*ExternalPropertyFileReference `json:"taxonomies,omitempty"` + ThreadFlowLocations []*ExternalPropertyFileReference `json:"threadFlowLocations,omitempty"` + Translations []*ExternalPropertyFileReference `json:"translations,omitempty"` + WebRequests []*ExternalPropertyFileReference `json:"webRequests,omitempty"` + WebResponses []*ExternalPropertyFileReference `json:"webResponses,omitempty"` +} + +// NewExternalPropertyFileReferences creates a new ExternalPropertyFileReferences and returns a pointer to it +func NewExternalPropertyFileReferences() *ExternalPropertyFileReferences { + return &ExternalPropertyFileReferences{} +} + +// WithAddress sets the Address +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithAddress(addresses []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Addresses = addresses + return externalPropertyFileReferences +} + +// AddAddress ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddAddress(address *ExternalPropertyFileReference) { + externalPropertyFileReferences.Addresses = append(externalPropertyFileReferences.Addresses, address) +} + +// WithArtifact sets the Artifact +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithArtifact(artifacts []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Artifacts = artifacts + return externalPropertyFileReferences +} + +// AddArtifact ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddArtifact(artifact *ExternalPropertyFileReference) { + externalPropertyFileReferences.Artifacts = append(externalPropertyFileReferences.Artifacts, artifact) +} + +// WithConversion sets the Conversion +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithConversion(conversion *ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Conversion = conversion + return externalPropertyFileReferences +} + +// WithDriver sets the Driver +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithDriver(driver *ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Driver = driver + return externalPropertyFileReferences +} + +// WithExtensions sets the Extensions +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithExtensions(extensions []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Extensions = extensions + return externalPropertyFileReferences +} + +// AddExtension ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddExtension(extension *ExternalPropertyFileReference) { + externalPropertyFileReferences.Extensions = append(externalPropertyFileReferences.Extensions, extension) +} + +// WithExternalizedProperties sets the ExternalizedProperties +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithExternalizedProperties(externalizedProperties *ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.ExternalizedProperties = externalizedProperties + return externalPropertyFileReferences +} + +// WithGraphs sets the Graphs +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithGraphs(graphs []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Graphs = graphs + return externalPropertyFileReferences +} + +// AddGraph ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddGraph(graph *ExternalPropertyFileReference) { + externalPropertyFileReferences.Graphs = append(externalPropertyFileReferences.Graphs, graph) +} + +// WithInvocations sets the Invocations +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithInvocations(invocations []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Invocations = invocations + return externalPropertyFileReferences +} + +// AddInvocation ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddInvocation(invocation *ExternalPropertyFileReference) { + externalPropertyFileReferences.Invocations = append(externalPropertyFileReferences.Invocations, invocation) +} + +// WithLogicalLocations sets the LogicalLocations +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithLogicalLocations(logicalLocations []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.LogicalLocations = logicalLocations + return externalPropertyFileReferences +} + +// AddLogicalLocation ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddLogicalLocation(logicalLocation *ExternalPropertyFileReference) { + externalPropertyFileReferences.LogicalLocations = append(externalPropertyFileReferences.LogicalLocations, logicalLocation) +} + +// WithPolicies sets the Policies +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithPolicies(policies []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Policies = policies + return externalPropertyFileReferences +} + +// AddPolicy ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddPolicy(policy *ExternalPropertyFileReference) { + externalPropertyFileReferences.Policies = append(externalPropertyFileReferences.Policies, policy) +} + +// WithResults sets the Results +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithResults(results []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Results = results + return externalPropertyFileReferences +} + +// AddResult ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddResult(result *ExternalPropertyFileReference) { + externalPropertyFileReferences.Results = append(externalPropertyFileReferences.Results, result) +} + +// WithTaxonomies sets the Taxonomies +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithTaxonomies(taxonomies []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Taxonomies = taxonomies + return externalPropertyFileReferences +} + +// AddTaxonomie ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddTaxonomie(taxonomy *ExternalPropertyFileReference) { + externalPropertyFileReferences.Taxonomies = append(externalPropertyFileReferences.Taxonomies, taxonomy) +} + +// WithThreadFlowLocations sets the ThreadFlowLocations +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithThreadFlowLocations(threadFlowLocations []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.ThreadFlowLocations = threadFlowLocations + return externalPropertyFileReferences +} + +// AddThreadFlowLocations ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddThreadFlowLocations(threadFlowLocation *ExternalPropertyFileReference) { + externalPropertyFileReferences.ThreadFlowLocations = append(externalPropertyFileReferences.ThreadFlowLocations, threadFlowLocation) +} + +// WithTranslations sets the Translations +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithTranslations(translation []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.Translations = translation + return externalPropertyFileReferences +} + +// AddTranslation ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddTranslation(translation *ExternalPropertyFileReference) { + externalPropertyFileReferences.Translations = append(externalPropertyFileReferences.Translations, translation) +} + +// WithWebRequests sets the WebRequests +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithWebRequests(webRequests []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.WebRequests = webRequests + return externalPropertyFileReferences +} + +// AddWebRequest ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddWebRequest(webRequest *ExternalPropertyFileReference) { + externalPropertyFileReferences.WebRequests = append(externalPropertyFileReferences.WebRequests, webRequest) +} + +// WithWebResponses sets the WebResponses +func (externalPropertyFileReferences *ExternalPropertyFileReferences) WithWebResponses(webResponses []*ExternalPropertyFileReference) *ExternalPropertyFileReferences { + externalPropertyFileReferences.WebResponses = webResponses + return externalPropertyFileReferences +} + +// AddWebResponse ... +func (externalPropertyFileReferences *ExternalPropertyFileReferences) AddWebResponse(webResponse *ExternalPropertyFileReference) { + externalPropertyFileReferences.WebResponses = append(externalPropertyFileReferences.WebResponses, webResponse) +} diff --git a/v2/sarif/fix.go b/v2/sarif/fix.go new file mode 100644 index 0000000..93ad563 --- /dev/null +++ b/v2/sarif/fix.go @@ -0,0 +1,48 @@ +package sarif + +// Fix ... +type Fix struct { + PropertyBag + Description *Message `json:"description,omitempty"` + ArtifactChanges []*ArtifactChange `json:"artifactChanges"` // required +} + +// NewFix creates a new Fix and returns a pointer to it +func NewFix() *Fix { + return &Fix{} +} + +// WithDescription sets the Description +func (fix *Fix) WithDescription(message *Message) *Fix { + fix.Description = message + return fix +} + +// WithDescriptionText sets the DescriptionText +func (fix *Fix) WithDescriptionText(text string) *Fix { + if fix.Description == nil { + fix.Description = &Message{} + } + fix.Description.Text = &text + return fix +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (fix *Fix) WithDescriptionMarkdown(markdown string) *Fix { + if fix.Description == nil { + fix.Description = &Message{} + } + fix.Description.Markdown = &markdown + return fix +} + +// WithArtifactChanges sets the ArtifactChanges +func (fix *Fix) WithArtifactChanges(artifactChanges []*ArtifactChange) *Fix { + fix.ArtifactChanges = artifactChanges + return fix +} + +// AddArtifactChanges ... +func (fix *Fix) AddArtifactChanges(artifactChange *ArtifactChange) { + fix.ArtifactChanges = append(fix.ArtifactChanges, artifactChange) +} diff --git a/v2/sarif/fix_test.go b/v2/sarif/fix_test.go new file mode 100644 index 0000000..0162fa3 --- /dev/null +++ b/v2/sarif/fix_test.go @@ -0,0 +1,26 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_simple_fix(t *testing.T) { + fix := NewFix(). + WithDescription(NewMessage(). + WithText("fix text")). + WithArtifactChanges([]*ArtifactChange{ + NewArtifactChange( + NewArtifactLocation(). + WithUri("file://broken.go"), + ).WithReplacement( + NewReplacement(NewRegion(). + WithStartLine(10). + WithEndLine(11), + ), + ), + }) + + assert.Equal(t, `{"description":{"text":"fix text"},"artifactChanges":[{"artifactLocation":{"uri":"file://broken.go"},"replacements":[{"deletedRegion":{"startLine":10,"endLine":11}}]}]}`, getJsonString(fix)) +} diff --git a/v2/sarif/graph.go b/v2/sarif/graph.go new file mode 100644 index 0000000..3f51ea9 --- /dev/null +++ b/v2/sarif/graph.go @@ -0,0 +1,60 @@ +package sarif + +// Graph ... +type Graph struct { + PropertyBag + Description *Message `json:"description,omitempty"` + Edges []*Edge `json:"edges,omitempty"` + Nodes []*Node `json:"nodes,omitempty"` +} + +// NewGraph creates a new Graph and returns a pointer to it +func NewGraph() *Graph { + return &Graph{} +} + +// WithDescription sets the Description +func (graph *Graph) WithDescription(message *Message) *Graph { + graph.Description = message + return graph +} + +// WithDescriptionText sets the DescriptionText +func (graph *Graph) WithDescriptionText(text string) *Graph { + if graph.Description == nil { + graph.Description = &Message{} + } + graph.Description.Text = &text + return graph +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (graph *Graph) WithDescriptionMarkdown(markdown string) *Graph { + if graph.Description == nil { + graph.Description = &Message{} + } + graph.Description.Markdown = &markdown + return graph +} + +// WithEdges sets the Edges +func (graph *Graph) WithEdges(edges []*Edge) *Graph { + graph.Edges = edges + return graph +} + +// AddEdge ... +func (graph *Graph) AddEdge(edge *Edge) { + graph.Edges = append(graph.Edges, edge) +} + +// WithNodes sets the Nodes +func (graph *Graph) WithNodes(nodes []*Node) *Graph { + graph.Nodes = nodes + return graph +} + +// AddNode ... +func (graph *Graph) AddNode(node *Node) { + graph.Nodes = append(graph.Nodes, node) +} diff --git a/v2/sarif/graph_traversal.go b/v2/sarif/graph_traversal.go new file mode 100644 index 0000000..9903721 --- /dev/null +++ b/v2/sarif/graph_traversal.go @@ -0,0 +1,76 @@ +package sarif + +// GraphTraversal ... +type GraphTraversal struct { + PropertyBag + Description *Message `json:"description,omitempty"` + EdgeTraversals []*EdgeTraversal `json:"edgeTraversals,omitempty"` + ImmutableState map[string]*MultiformatMessageString `json:"immutableState,omitempty"` + InitialState map[string]*MultiformatMessageString `json:"initialState,omitempty"` + ResultGraphIndex *int `json:"resultGraphIndex,omitempty"` + RunGraphIndex *int `json:"runGraphIndex,omitempty"` +} + +// NewGraphTraversal creates a new GraphTraversal and returns a pointer to it +func NewGraphTraversal() *GraphTraversal { + return &GraphTraversal{} +} + +// WithDescription sets the Description +func (graphTraversal *GraphTraversal) WithDescription(message *Message) *GraphTraversal { + graphTraversal.Description = message + return graphTraversal +} + +// WithDescriptionText sets the DescriptionText +func (graphTraversal *GraphTraversal) WithDescriptionText(text string) *GraphTraversal { + if graphTraversal.Description == nil { + graphTraversal.Description = &Message{} + } + graphTraversal.Description.Text = &text + return graphTraversal +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (graphTraversal *GraphTraversal) WithDescriptionMarkdown(markdown string) *GraphTraversal { + if graphTraversal.Description == nil { + graphTraversal.Description = &Message{} + } + graphTraversal.Description.Markdown = &markdown + return graphTraversal +} + +// WithEdgeTraversals sets the EdgeTraversals +func (graphTraversal *GraphTraversal) WithEdgeTraversals(edgeTraversals []*EdgeTraversal) *GraphTraversal { + graphTraversal.EdgeTraversals = edgeTraversals + return graphTraversal +} + +// AddEdge ... +func (graphTraversal *GraphTraversal) AddEdge(edgeTraversal *EdgeTraversal) { + graphTraversal.EdgeTraversals = append(graphTraversal.EdgeTraversals, edgeTraversal) +} + +// WithResultGraphIndex sets the ResultGraphIndex +func (graphTraversal *GraphTraversal) WithResultGraphIndex(index int) *GraphTraversal { + graphTraversal.ResultGraphIndex = &index + return graphTraversal +} + +// WithRunGraphIndex sets the RunGraphIndex +func (graphTraversal *GraphTraversal) WithRunGraphIndex(index int) *GraphTraversal { + graphTraversal.RunGraphIndex = &index + return graphTraversal +} + +// WithImmutableState sets the ImmutableState +func (graphTraversal *GraphTraversal) WithImmutableState(immutableState map[string]*MultiformatMessageString) *GraphTraversal { + graphTraversal.ImmutableState = immutableState + return graphTraversal +} + +// WithInitialState sets the InitialState +func (graphTraversal *GraphTraversal) WithInitialState(initialState map[string]*MultiformatMessageString) *GraphTraversal { + graphTraversal.InitialState = initialState + return graphTraversal +} diff --git a/v2/sarif/invocation.go b/v2/sarif/invocation.go new file mode 100644 index 0000000..1228d99 --- /dev/null +++ b/v2/sarif/invocation.go @@ -0,0 +1,218 @@ +package sarif + +import "time" + +// Invocation describes the runtime environment of the analysis tool run. +type Invocation struct { + PropertyBag + Account *string `json:"account,omitempty"` + Arguments []string `json:"arguments,omitempty"` + CommandLine *string `json:"commandLine,omitempty"` + EndTimeUTC *time.Time `json:"endTimeUtc,omitempty"` + EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"` + ExecutableLocation *ArtifactLocation `json:"executableLocation,omitempty"` + ExecutionSuccessful *bool `json:"executionSuccessful"` + ExitCode *int `json:"exitCode,omitempty"` + ExitCodeDescription *string `json:"exitCodeDescription,omitempty"` + ExitSignalName *string `json:"exitSignalName,omitempty"` + ExitSignalNumber *int `json:"exitSignalNumber,omitempty"` + Machine *string `json:"machine,omitempty"` + NotificationConfigurationOverrides []*ConfigurationOverride `json:"notificationConfigurationOverrides,omitempty"` + ProcessID *int `json:"processId,omitempty"` + ProcessStartFailureMessage *string `json:"processStartFailureMessage,omitempty"` + ResponseFiles []*ArtifactLocation `json:"responseFiles,omitempty"` + RuleConfigurationOverrides []*ConfigurationOverride `json:"ruleConfigurationOverrides,omitempty"` + StartTimeUTC *time.Time `json:"startTimeUtc,omitempty"` + Stderr *ArtifactLocation `json:"stderr,omitempty"` + Stdin *ArtifactLocation `json:"stdin,omitempty"` + Stdout *ArtifactLocation `json:"stdout,omitempty"` + StdoutStderr *ArtifactLocation `json:"stdoutStderr,omitempty"` + ToolConfigurationNotifications []*Notification `json:"toolConfigurationNotifications,omitempty"` + ToolExecutionNotifications []*Notification `json:"toolExecutionNotifications,omitempty"` + WorkingDirectory *ArtifactLocation `json:"workingDirectory,omitempty"` +} + +// NewInvocation creates a new Invocation and returns a pointer to it +func NewInvocation() *Invocation { + return &Invocation{} +} + +// WithAccount sets the Account +func (invocation *Invocation) WithAccount(account string) *Invocation { + invocation.Account = &account + return invocation +} + +// WithArguments sets the Arguments +func (invocation *Invocation) WithArguments(arguments []string) *Invocation { + invocation.Arguments = arguments + return invocation +} + +// AddArgument ... +func (invocation *Invocation) AddArgument(argument string) { + invocation.Arguments = append(invocation.Arguments, argument) +} + +// WithCommanLine sets the CommanLine +func (invocation *Invocation) WithCommanLine(commandLine string) *Invocation { + invocation.CommandLine = &commandLine + return invocation +} + +// WithEndTimeUTC sets the instant when the invocation ended and returns the same Invocation. +func (invocation *Invocation) WithEndTimeUTC(endTime time.Time) *Invocation { + endTimeUTC := endTime.UTC() + invocation.EndTimeUTC = &endTimeUTC + return invocation +} + +// WithEnvironmentVariables sets the EnvironmentVariables +func (invocation *Invocation) WithEnvironmentVariables(environmentVariables map[string]string) *Invocation { + invocation.EnvironmentVariables = environmentVariables + return invocation +} + +// SetEnvironmentVariable ... +func (invocation *Invocation) SetEnvironmentVariable(name, value string) { + invocation.EnvironmentVariables[name] = value +} + +// WithExecutableLocation sets the ExecutableLocation +func (invocation *Invocation) WithExecutableLocation(executableLocation *ArtifactLocation) *Invocation { + invocation.ExecutableLocation = executableLocation + return invocation +} +// WithExecutionSuccess sets the ExecutionSuccess +func (invocation *Invocation) WithExecutionSuccess(executionSuccessful bool) *Invocation { + invocation.ExecutionSuccessful = &executionSuccessful + return invocation +} +// WithExitCode sets the ExitCode +func (invocation *Invocation) WithExitCode(exitCode int) *Invocation { + invocation.ExitCode = &exitCode + return invocation +} +// WithExitCodeDescription sets the ExitCodeDescription +func (invocation *Invocation) WithExitCodeDescription(exitCodeDescription string) *Invocation { + invocation.ExitCodeDescription = &exitCodeDescription + return invocation +} +// WithExitSignalNumber sets the ExitSignalNumber +func (invocation *Invocation) WithExitSignalNumber(exitSignalNumber int) *Invocation { + invocation.ExitSignalNumber = &exitSignalNumber + return invocation +} +// WithExitSignalName sets the ExitSignalName +func (invocation *Invocation) WithExitSignalName(exitSignalName string) *Invocation { + invocation.ExitSignalName = &exitSignalName + return invocation +} +// WithMachine sets the Machine +func (invocation *Invocation) WithMachine(machine string) *Invocation { + invocation.Machine = &machine + return invocation +} + +// WithNotificationConfigurationOverrides sets the NotificationConfigurationOverrides +func (invocation *Invocation) WithNotificationConfigurationOverrides(overrides []*ConfigurationOverride) *Invocation { + invocation.NotificationConfigurationOverrides = overrides + return invocation +} + +// AddNotificationConfigurationOverride ... +func (invocation *Invocation) AddNotificationConfigurationOverride(override *ConfigurationOverride) { + invocation.NotificationConfigurationOverrides = append(invocation.NotificationConfigurationOverrides, override) +} + +// WithProcessID sets the ProcessID +func (invocation *Invocation) WithProcessID(processID int) *Invocation { + invocation.ProcessID = &processID + + return invocation +} +// WithProcessStartFailureMessage sets the ProcessStartFailureMessage +func (invocation *Invocation) WithProcessStartFailureMessage(failureMessage string) *Invocation { + invocation.ProcessStartFailureMessage = &failureMessage + return invocation +} + +// WithResponseFiles sets the ResponseFiles +func (invocation *Invocation) WithResponseFiles(responseFiles []*ArtifactLocation) *Invocation { + invocation.ResponseFiles = responseFiles + return invocation +} + +// AddResponseFile ... +func (invocation *Invocation) AddResponseFile(responseFile *ArtifactLocation) { + invocation.ResponseFiles = append(invocation.ResponseFiles, responseFile) +} + +// WithRuleConfigurationOverrides sets the RuleConfigurationOverrides +func (invocation *Invocation) WithRuleConfigurationOverrides(overrides []*ConfigurationOverride) *Invocation { + invocation.RuleConfigurationOverrides = overrides + return invocation +} + +// AddRuleConfigurationOverride ... +func (invocation *Invocation) AddRuleConfigurationOverride(override *ConfigurationOverride) { + invocation.RuleConfigurationOverrides = append(invocation.RuleConfigurationOverrides, override) +} + +// WithStartTimeUTC sets the instant when the invocation started and returns the same Invocation. +func (invocation *Invocation) WithStartTimeUTC(startTime time.Time) *Invocation { + startTimeUTC := startTime.UTC() + invocation.StartTimeUTC = &startTimeUTC + return invocation +} + +// WithStdErr sets the StdErr +func (invocation *Invocation) WithStdErr(stdErr *ArtifactLocation) *Invocation { + invocation.Stderr = stdErr + return invocation +} + +// WithStdIn sets the StdIn +func (invocation *Invocation) WithStdIn(stdIn *ArtifactLocation) *Invocation { + invocation.Stdin = stdIn + return invocation +} + +// WithStdout sets the Stdout +func (invocation *Invocation) WithStdout(stdOut *ArtifactLocation) *Invocation { + invocation.Stdout = stdOut + return invocation +} +// WithStdoutStderr sets the StdoutStderr +func (invocation *Invocation) WithStdoutStderr(stdoutStderr *ArtifactLocation) *Invocation { + invocation.StdoutStderr = stdoutStderr + return invocation +} + +// WithToolConfigurationNotifications sets the ToolConfigurationNotifications +func (invocation *Invocation) WithToolConfigurationNotifications(toolConfigNotifications []*Notification) *Invocation { + invocation.ToolConfigurationNotifications = toolConfigNotifications + return invocation +} + +// AddToolConfigurationNotification ... +func (invocation *Invocation) AddToolConfigurationNotification(toolConfigNotification *Notification) { + invocation.ToolConfigurationNotifications = append(invocation.ToolConfigurationNotifications, toolConfigNotification) +} + +// WithToolExecutionNotifications sets the ToolExecutionNotifications +func (invocation *Invocation) WithToolExecutionNotifications(toolExecutionNotification []*Notification) *Invocation { + invocation.ToolExecutionNotifications = toolExecutionNotification + return invocation +} + +// AddTToolExecutionNotification ... +func (invocation *Invocation) AddTToolExecutionNotification(toolExecutionNotification *Notification) { + invocation.ToolExecutionNotifications = append(invocation.ToolExecutionNotifications, toolExecutionNotification) +} + +// WithWorkingDirectory sets the current working directory of the invocation and returns the same Invocation. +func (invocation *Invocation) WithWorkingDirectory(workingDirectory *ArtifactLocation) *Invocation { + invocation.WorkingDirectory = workingDirectory + return invocation +} diff --git a/v2/sarif/invocation_test.go b/v2/sarif/invocation_test.go new file mode 100644 index 0000000..54d91ab --- /dev/null +++ b/v2/sarif/invocation_test.go @@ -0,0 +1,24 @@ +package sarif + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_invocation_with_time_utc(t *testing.T) { + success := true + i := (&Invocation{ExecutionSuccessful: &success}). + WithStartTimeUTC(mustParseTime(t, "2020-12-31T23:59:59+01:00")). + WithEndTimeUTC(mustParseTime(t, "2021-01-01T00:00:00+01:00")) + + assert.Equal(t, `{"endTimeUtc":"2020-12-31T23:00:00Z","executionSuccessful":true,"startTimeUtc":"2020-12-31T22:59:59Z"}`, getJsonString(i)) +} + +func mustParseTime(t *testing.T, value string) time.Time { + ts, err := time.Parse(time.RFC3339, value) + require.NoError(t, err) + return ts +} diff --git a/v2/sarif/location.go b/v2/sarif/location.go new file mode 100644 index 0000000..df8c7af --- /dev/null +++ b/v2/sarif/location.go @@ -0,0 +1,92 @@ +package sarif + +// Location ... +type Location struct { + PropertyBag + Id *uint `json:"id,omitempty"` + PhysicalLocation *PhysicalLocation `json:"physicalLocation,omitempty"` + LogicalLocations []*LogicalLocation `json:"logicalLocations,omitempty"` + Message *Message `json:"message,omitempty"` + Annotations []*Region `json:"annotations,omitempty"` + Relationships []*LocationRelationship `json:"relationships,omitempty"` +} + +// NewLocation creates a new Location and returns a pointer to it +func NewLocation() *Location { + return &Location{} +} + +// NewLocationWithPhysicalLocation creates a new LocationWithPhysicalLocation and returns a pointer to it +func NewLocationWithPhysicalLocation(physicalLocation *PhysicalLocation) *Location { + return NewLocation().WithPhysicalLocation(physicalLocation) +} + +// WithId sets the Id +func (location *Location) WithId(id int) *Location { + i := uint(id) + location.Id = &i + return location +} + +// WithPhysicalLocation sets the PhysicalLocation +func (location *Location) WithPhysicalLocation(physicalLocation *PhysicalLocation) *Location { + location.PhysicalLocation = physicalLocation + return location +} + +// WithLogicalLocations sets the LogicalLocations +func (location *Location) WithLogicalLocations(logicalLocations []*LogicalLocation) *Location { + location.LogicalLocations = logicalLocations + return location +} + +// AddLogicalLocations ... +func (location *Location) AddLogicalLocations(logicalLocation *LogicalLocation) { + location.LogicalLocations = append(location.LogicalLocations, logicalLocation) +} + +// WithMessage sets the Message +func (location *Location) WithMessage(message *Message) *Location { + location.Message = message + return location +} + +// WithDescriptionText sets the DescriptionText +func (location *Location) WithDescriptionText(text string) *Location { + if location.Message == nil { + location.Message = &Message{} + } + location.Message.Text = &text + return location +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (location *Location) WithDescriptionMarkdown(markdown string) *Location { + if location.Message == nil { + location.Message = &Message{} + } + location.Message.Markdown = &markdown + return location +} + +// WithAnnotations sets the Annotations +func (location *Location) WithAnnotations(annotations []*Region) *Location { + location.Annotations = annotations + return location +} + +// AddAnnotation ... +func (location *Location) AddAnnotation(annotation *Region) { + location.Annotations = append(location.Annotations, annotation) +} + +// WithRelationships sets the Relationships +func (location *Location) WithRelationships(locationRelationships []*LocationRelationship) *Location { + location.Relationships = locationRelationships + return location +} + +// AddRelationship ... +func (location *Location) AddRelationship(locationRelationship *LocationRelationship) { + location.Relationships = append(location.Relationships, locationRelationship) +} diff --git a/v2/sarif/location_relationship.go b/v2/sarif/location_relationship.go new file mode 100644 index 0000000..38baf7a --- /dev/null +++ b/v2/sarif/location_relationship.go @@ -0,0 +1,52 @@ +package sarif + +// LocationRelationship ... +type LocationRelationship struct { + PropertyBag + Target uint `json:"target"` + Kinds []string `json:"kinds,omitempty"` + Description *Message `json:"description,omitempty"` +} + +// NewLocationRelationship creates a new LocationRelationship and returns a pointer to it +func NewLocationRelationship(target int) *LocationRelationship { + t := uint(target) + return &LocationRelationship{ + Target: t, + } +} + +// WithKinds sets the Kinds +func (locationRelationship *LocationRelationship) WithKinds(kinds []string) *LocationRelationship { + locationRelationship.Kinds = kinds + return locationRelationship +} + +// AddKind ... +func (locationRelationship *LocationRelationship) AddKind(kind string) { + locationRelationship.Kinds = append(locationRelationship.Kinds, kind) +} + +// WithDescription sets the Description +func (locationRelationship *LocationRelationship) WithDescription(message *Message) *LocationRelationship { + locationRelationship.Description = message + return locationRelationship +} + +// WithDescriptionText sets the DescriptionText +func (locationRelationship *LocationRelationship) WithDescriptionText(text string) *LocationRelationship { + if locationRelationship.Description == nil { + locationRelationship.Description = &Message{} + } + locationRelationship.Description.Text = &text + return locationRelationship +} + +// WithDescriptionMarkdown sets the DescriptionMarkdown +func (locationRelationship *LocationRelationship) WithDescriptionMarkdown(markdown string) *LocationRelationship { + if locationRelationship.Description == nil { + locationRelationship.Description = &Message{} + } + locationRelationship.Description.Markdown = &markdown + return locationRelationship +} diff --git a/v2/sarif/location_relationship_test.go b/v2/sarif/location_relationship_test.go new file mode 100644 index 0000000..cae7c76 --- /dev/null +++ b/v2/sarif/location_relationship_test.go @@ -0,0 +1,18 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_new_location_relationship(t *testing.T) { + l := NewLocationRelationship(1). + WithDescription( + NewMessage(). + WithMarkdown("# markdown text"), + ). + WithKinds([]string{"kind", "another kind"}) + + assert.Equal(t, `{"target":1,"kinds":["kind","another kind"],"description":{"markdown":"# markdown text"}}`, getJsonString(l)) +} diff --git a/v2/sarif/location_test.go b/v2/sarif/location_test.go new file mode 100644 index 0000000..f80fa6e --- /dev/null +++ b/v2/sarif/location_test.go @@ -0,0 +1,22 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_simple_location(t *testing.T) { + l := NewLocation(). + WithId(1). + WithMessage( + NewMessage(). + WithId("messageId1"). + WithText("message text")). + WithAnnotations( + []*Region{NewRegion().WithByteLength(10)}, + ). + WithRelationships([]*LocationRelationship{NewLocationRelationship(1)}) + + assert.Equal(t, `{"id":1,"message":{"text":"message text","id":"messageId1"},"annotations":[{"byteLength":10}],"relationships":[{"target":1}]}`, getJsonString(l)) +} diff --git a/v2/sarif/logical_location.go b/v2/sarif/logical_location.go new file mode 100644 index 0000000..afe90db --- /dev/null +++ b/v2/sarif/logical_location.go @@ -0,0 +1,55 @@ +package sarif + +// LogicalLocation ... +type LogicalLocation struct { + PropertyBag + Index *uint `json:"index,omitempty"` + Name *string `json:"name,omitempty"` + FullyQualifiedName *string `json:"fullyQualifiedName,omitempty"` + DecoratedName *string `json:"decoratedName,omitempty"` + Kind *string `json:"kind,omitempty"` + ParentIndex *uint `json:"parentIndex,omitempty"` +} + +// NewLogicalLocation creates a new LogicalLocation and returns a pointer to it +func NewLogicalLocation() *LogicalLocation { + return &LogicalLocation{} +} + +// WithIndex sets the Index +func (l *LogicalLocation) WithIndex(index int) *LogicalLocation { + i := uint(index) + l.Index = &i + return l +} + +// WithName sets the Name +func (l *LogicalLocation) WithName(name string) *LogicalLocation { + l.Name = &name + return l +} + +// WithFullyQualifiedName sets the FullyQualifiedName +func (l *LogicalLocation) WithFullyQualifiedName(fullyQualifiedName string) *LogicalLocation { + l.FullyQualifiedName = &fullyQualifiedName + return l +} + +// WithDecoratedName sets the DecoratedName +func (l *LogicalLocation) WithDecoratedName(decoratedName string) *LogicalLocation { + l.DecoratedName = &decoratedName + return l +} + +// WithKind sets the Kind +func (l *LogicalLocation) WithKind(kind string) *LogicalLocation { + l.Kind = &kind + return l +} + +// WithParentIndex sets the ParentIndex +func (l *LogicalLocation) WithParentIndex(parentIndex int) *LogicalLocation { + i := uint(parentIndex) + l.ParentIndex = &i + return l +} diff --git a/v2/sarif/logical_location_test.go b/v2/sarif/logical_location_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/logical_location_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/message.go b/v2/sarif/message.go new file mode 100644 index 0000000..0c3ee94 --- /dev/null +++ b/v2/sarif/message.go @@ -0,0 +1,54 @@ +package sarif + +// Message ... +type Message struct { + PropertyBag + Text *string `json:"text,omitempty"` + Markdown *string `json:"markdown,omitempty"` + Id *string `json:"id,omitempty"` + Arguments []string `json:"arguments,omitempty"` +} + +// NewMessage creates a new Message and returns a pointer to it +func NewMessage() *Message { + return &Message{} +} + +// NewTextMessage creates a new TextMessage and returns a pointer to it +func NewTextMessage(text string) *Message { + return NewMessage().WithText(text) +} + +// NewMarkdownMessage creates a new MarkdownMessage and returns a pointer to it +func NewMarkdownMessage(markdown string) *Message { + return NewMessage().WithMarkdown(markdown) +} + +// WithText sets the Text +func (message *Message) WithText(text string) *Message { + message.Text = &text + return message +} + +// WithMarkdown sets the Markdown +func (message *Message) WithMarkdown(markdown string) *Message { + message.Markdown = &markdown + return message +} + +// WithId sets the Id +func (message *Message) WithId(id string) *Message { + message.Id = &id + return message +} + +// WithArguments sets the Arguments +func (message *Message) WithArguments(arguments []string) *Message { + message.Arguments = arguments + return message +} + +// AddArgument ... +func (message *Message) AddArgument(argument string) { + message.Arguments = append(message.Arguments, argument) +} diff --git a/v2/sarif/message_test.go b/v2/sarif/message_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/message_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/multi_format_message_string.go b/v2/sarif/multi_format_message_string.go new file mode 100644 index 0000000..6191d64 --- /dev/null +++ b/v2/sarif/multi_format_message_string.go @@ -0,0 +1,33 @@ +package sarif + +// MultiformatMessageString ... +type MultiformatMessageString struct { + PropertyBag + Text *string `json:"text,omitempty"` + Markdown *string `json:"markdown,omitempty"` +} + +// NewMarkdownMultiformatMessageString creates a new MarkdownMultiformatMessageString and returns a pointer to it +func NewMarkdownMultiformatMessageString(markdown string) *MultiformatMessageString { + return &MultiformatMessageString{ + Markdown: &markdown, + } +} + +// NewMultiformatMessageString creates a new MultiformatMessageString and returns a pointer to it +func NewMultiformatMessageString(text string) *MultiformatMessageString { + return &MultiformatMessageString{ + Text: &text, + } +} + +// WithText sets the Text +func (multiFormatMessageString *MultiformatMessageString) WithText(text string) *MultiformatMessageString { + multiFormatMessageString.Text = &text + return multiFormatMessageString +} +// WithMarkdown sets the Markdown +func (multiFormatMessageString *MultiformatMessageString) WithMarkdown(markdown string) *MultiformatMessageString { + multiFormatMessageString.Markdown = &markdown + return multiFormatMessageString +} diff --git a/v2/sarif/multi_format_message_string_test.go b/v2/sarif/multi_format_message_string_test.go new file mode 100644 index 0000000..18d55e5 --- /dev/null +++ b/v2/sarif/multi_format_message_string_test.go @@ -0,0 +1,22 @@ +package sarif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_create_multi_format_message_string(t *testing.T) { + + msg := NewMultiformatMessageString("mock plain text") + + assert.Equal(t, `{"text":"mock plain text"}`, getJsonString(msg)) +} + +func Test_create_multi_format_message_string_with_markdown(t *testing.T) { + + msg := NewMultiformatMessageString("mock plain text"). + WithMarkdown("mock markdown text") + + assert.Equal(t, `{"text":"mock plain text","markdown":"mock markdown text"}`, getJsonString(msg)) +} diff --git a/v2/sarif/node.go b/v2/sarif/node.go new file mode 100644 index 0000000..20c5d5f --- /dev/null +++ b/v2/sarif/node.go @@ -0,0 +1,52 @@ +package sarif + +// Node ... +type Node struct { + PropertyBag + Children []*Node `json:"children,omitempty"` + ID string `json:"id"` + Label *Message `json:"label,omitempty"` + Location *Location `json:"location,omitempty"` +} + +// NewNode creates a new Node and returns a pointer to it +func NewNode(id string) *Node { + return &Node{ + ID: id, + } +} + +// WithChildren sets the Children +func (node *Node) WithChildren(children []*Node) *Node { + node.Children = children + return node +} + +// AddChild ... +func (node *Node) AddChild(child *Node) { + node.Children = append(node.Children, child) +} + +// WithLabel sets the Label +func (node *Node) WithLabel(message *Message) *Node { + node.Label = message + return node +} + +// WithLabelText sets the LabelText +func (node *Node) WithLabelText(text string) *Node { + if node.Label == nil { + node.Label = &Message{} + } + node.Label.Text = &text + return node +} + +// WithLabelMarkdown sets the LabelMarkdown +func (node *Node) WithLabelMarkdown(markdown string) *Node { + if node.Label == nil { + node.Label = &Message{} + } + node.Label.Markdown = &markdown + return node +} diff --git a/v2/sarif/notification.go b/v2/sarif/notification.go new file mode 100644 index 0000000..0cf5cf9 --- /dev/null +++ b/v2/sarif/notification.go @@ -0,0 +1,98 @@ +package sarif + +import "time" + +// Notification ... +type Notification struct { + PropertyBag + AssociatedRule *ReportingDescriptorReference `json:"associatedRule,omitempty"` + Descriptor *ReportingDescriptorReference `json:"descriptor,omitempty"` + Exception *Exception `json:"exception,omitempty"` + Level string `json:"level,omitempty"` + Locations []*Location `json:"locations,omitempty"` + Message *Message `json:"message"` + ThreadID *int `json:"threadId,omitempty"` + TimeUTC *time.Time `json:"timeUtc,omitempty"` +} + +// NewNotification creates a new Notification and returns a pointer to it +func NewNotification() *Notification { + return &Notification{} +} + +// WithAssociatedRule sets the AssociatedRule +func (notification *Notification) WithAssociatedRule(associatedRule *ReportingDescriptorReference) *Notification { + notification.AssociatedRule = associatedRule + + return notification +} + +// WithDescriptor sets the Descriptor +func (notification *Notification) WithDescriptor(descriptor *ReportingDescriptorReference) *Notification { + notification.Descriptor = descriptor + + return notification +} + +// WithException sets the Exception +func (notification *Notification) WithException(exception *Exception) *Notification { + notification.Exception = exception + + return notification +} + +// WithLevel sets the Level +func (notification *Notification) WithLevel(level string) *Notification { + notification.Level = level + + return notification +} + +// WithLocations sets the Locations +func (notification *Notification) WithLocations(locations []*Location) *Notification { + notification.Locations = locations + + return notification +} + +// AddLocation ... +func (notification *Notification) AddLocation(location *Location) { + notification.Locations = append(notification.Locations, location) +} + +// WithMessage sets the Message +func (notification *Notification) WithMessage(message *Message) *Notification { + notification.Message = message + return notification +} + +// WithTextMessage sets the Message text +func (notification *Notification) WithTextMessage(text string) *Notification { + if notification.Message == nil { + notification.Message = &Message{} + } + notification.Message.Text = &text + return notification +} + +// WithMessageMarkdown sets the Message markdown +func (notification *Notification) WithMessageMarkdown(markdown string) *Notification { + if notification.Message == nil { + notification.Message = &Message{} + } + notification.Message.Markdown = &markdown + return notification +} + +// WithThreadID sets the ThreadID +func (notification *Notification) WithThreadID(threadID int) *Notification { + notification.ThreadID = &threadID + + return notification +} + +// WithTimeUTC sets the TimeUTC +func (notification *Notification) WithTimeUTC(timeUTC *time.Time) *Notification { + notification.TimeUTC = timeUTC + return notification +} diff --git a/v2/sarif/physical_location.go b/v2/sarif/physical_location.go new file mode 100644 index 0000000..838e555 --- /dev/null +++ b/v2/sarif/physical_location.go @@ -0,0 +1,38 @@ +package sarif + +// PhysicalLocation ... +type PhysicalLocation struct { + PropertyBag + ArtifactLocation *ArtifactLocation `json:"artifactLocation,omitempty"` + Region *Region `json:"region,omitempty"` + ContextRegion *Region `json:"contextRegion,omitempty"` + Address *Address `json:"address,omitempty"` +} + +// NewPhysicalLocation creates a new PhysicalLocation and returns a pointer to it +func NewPhysicalLocation() *PhysicalLocation { + return &PhysicalLocation{} +} + +// WithArtifactLocation sets the ArtifactLocation +func (pl *PhysicalLocation) WithArtifactLocation(artifactLocation *ArtifactLocation) *PhysicalLocation { + pl.ArtifactLocation = artifactLocation + return pl +} + +// WithRegion sets the Region +func (pl *PhysicalLocation) WithRegion(region *Region) *PhysicalLocation { + pl.Region = region + return pl +} +// WithContextRegion sets the ContextRegion +func (pl *PhysicalLocation) WithContextRegion(contextRegion *Region) *PhysicalLocation { + pl.ContextRegion = contextRegion + return pl +} + +// WithAddress sets the Address +func (pl *PhysicalLocation) WithAddress(address *Address) *PhysicalLocation { + pl.Address = address + return pl +} diff --git a/v2/sarif/physical_location_test.go b/v2/sarif/physical_location_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/physical_location_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/properties.go b/v2/sarif/properties.go new file mode 100644 index 0000000..e3d7a22 --- /dev/null +++ b/v2/sarif/properties.go @@ -0,0 +1,41 @@ +package sarif + +// Properties ... +type Properties map[string]interface{} + +// PropertyBag ... +type PropertyBag struct { + Properties Properties `json:"properties,omitempty"` +} + +// NewPropertyBag creates a new PropertyBag and returns a pointer to it +func NewPropertyBag() *PropertyBag { + return &PropertyBag{ + Properties: Properties{}, + } +} + +// Add ... +func (propertyBag *PropertyBag) Add(key string, value interface{}) { + propertyBag.Properties[key] = value +} + +// AddString ... +func (propertyBag *PropertyBag) AddString(key, value string) { + propertyBag.Add(key, value) +} + +// AddBoolean ... +func (propertyBag *PropertyBag) AddBoolean(key string, value bool) { + propertyBag.Add(key, value) +} + +// AddInteger ... +func (propertyBag *PropertyBag) AddInteger(key string, value int) { + propertyBag.Add(key, value) +} + +// AttachPropertyBag adds a property bag to a rule +func (propertyBag *PropertyBag) AttachPropertyBag(pb *PropertyBag) { + propertyBag.Properties = pb.Properties +} diff --git a/v2/sarif/rectangle.go b/v2/sarif/rectangle.go new file mode 100644 index 0000000..dcd3934 --- /dev/null +++ b/v2/sarif/rectangle.go @@ -0,0 +1,64 @@ +package sarif + +// Rectangle ... +type Rectangle struct { + PropertyBag + Bottom *float64 `json:"bottom,omitempty"` + Left *float64 `json:"left,omitempty"` + Message *Message `json:"message,omitempty"` + Right *float64 `json:"right,omitempty"` + Top *float64 `json:"top,omitempty"` +} + +// NewRectangle creates a new Rectangle and returns a pointer to it +func NewRectangle() *Rectangle { + return &Rectangle{} +} + +// WithBottom sets the Bottom +func (rectangle *Rectangle) WithBottom(bottom float64) *Rectangle { + rectangle.Bottom = &bottom + return rectangle +} + +// WithTop sets the Top +func (rectangle *Rectangle) WithTop(top float64) *Rectangle { + rectangle.Top = &top + return rectangle +} + +// WithLeft sets the Left +func (rectangle *Rectangle) WithLeft(withLeft float64) *Rectangle { + rectangle.Left = &withLeft + return rectangle +} + +// WithRight sets the Right +func (rectangle *Rectangle) WithRight(right float64) *Rectangle { + rectangle.Right = &right + return rectangle +} + +// WithMessage sets the Message +func (rectangle *Rectangle) WithMessage(message *Message) *Rectangle { + rectangle.Message = message + return rectangle +} + +// WithTextMessage sets the Message text +func (rectangle *Rectangle) WithTextMessage(text string) *Rectangle { + if rectangle.Message == nil { + rectangle.Message = &Message{} + } + rectangle.Message.Text = &text + return rectangle +} + +// WithMessageMarkdown sets the Message markdown +func (rectangle *Rectangle) WithMessageMarkdown(markdown string) *Rectangle { + if rectangle.Message == nil { + rectangle.Message = &Message{} + } + rectangle.Message.Markdown = &markdown + return rectangle +} diff --git a/v2/sarif/region.go b/v2/sarif/region.go new file mode 100644 index 0000000..3e64183 --- /dev/null +++ b/v2/sarif/region.go @@ -0,0 +1,113 @@ +package sarif + +// Region ... +type Region struct { + PropertyBag + StartLine *int `json:"startLine,omitempty"` + StartColumn *int `json:"startColumn,omitempty"` + EndLine *int `json:"endLine,omitempty"` + EndColumn *int `json:"endColumn,omitempty"` + CharOffset *int `json:"charOffset,omitempty"` + CharLength *int `json:"charLength,omitempty"` + ByteOffset *int `json:"byteOffset,omitempty"` + ByteLength *int `json:"byteLength,omitempty"` + Snippet *ArtifactContent `json:"snippet,omitempty"` + Message *Message `json:"message,omitempty"` + SourceLanguage *string `json:"sourceLanguage,omitempty"` +} + +// NewRegion creates a new Region and returns a pointer to it +func NewRegion() *Region { + return &Region{} +} + +// NewSimpleRegion creates a new SimpleRegion and returns a pointer to it +func NewSimpleRegion(startLine, endLine int) *Region { + return NewRegion(). + WithStartLine(startLine). + WithEndLine(endLine) +} + +// WithStartLine sets the StartLine +func (region *Region) WithStartLine(startLine int) *Region { + region.StartLine = &startLine + return region +} + +// WithStartColumn sets the StartColumn +func (region *Region) WithStartColumn(startColumn int) *Region { + region.StartColumn = &startColumn + return region +} + +// WithEndLine sets the EndLine +func (region *Region) WithEndLine(endLine int) *Region { + region.EndLine = &endLine + return region +} + +// WithEndColumn sets the EndColumn +func (region *Region) WithEndColumn(endColumn int) *Region { + region.EndColumn = &endColumn + return region +} + +// WithCharOffset sets the CharOffset +func (region *Region) WithCharOffset(charOffset int) *Region { + region.CharOffset = &charOffset + return region +} + +// WithCharLength sets the CharLength +func (region *Region) WithCharLength(charLength int) *Region { + region.CharLength = &charLength + return region +} + +// WithByteOffset sets the ByteOffset +func (region *Region) WithByteOffset(byteOffset int) *Region { + region.ByteOffset = &byteOffset + return region +} + +// WithByteLength sets the ByteLength +func (region *Region) WithByteLength(byteLength int) *Region { + region.ByteLength = &byteLength + return region +} + +// WithSnippet sets the Snippet +func (region *Region) WithSnippet(snippet *ArtifactContent) *Region { + region.Snippet = snippet + return region +} + +// WithMessage sets the Message +func (region *Region) WithMessage(message *Message) *Region { + region.Message = message + return region +} + +// WithTextMessage sets the Message text +func (region *Region) WithTextMessage(text string) *Region { + if region.Message == nil { + region.Message = &Message{} + } + region.Message.Text = &text + return region +} + +// WithMessageMarkdown sets the Message markdown +func (region *Region) WithMessageMarkdown(markdown string) *Region { + if region.Message == nil { + region.Message = &Message{} + } + region.Message.Markdown = &markdown + return region +} + +// WithSourceLanguage sets the SourceLanguage +func (region *Region) WithSourceLanguage(sourceLanguage string) *Region { + + return region +} diff --git a/v2/sarif/region_test.go b/v2/sarif/region_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/region_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/replacement.go b/v2/sarif/replacement.go new file mode 100644 index 0000000..682dc80 --- /dev/null +++ b/v2/sarif/replacement.go @@ -0,0 +1,21 @@ +package sarif + +// Replacement ... +type Replacement struct { + PropertyBag + DeletedRegion Region `json:"deletedRegion"` + InsertedContent *ArtifactContent `json:"insertedContent,omitempty"` +} + +// NewReplacement creates a new Replacement and returns a pointer to it +func NewReplacement(region *Region) *Replacement { + return &Replacement{ + DeletedRegion: *region, + } +} + +// WithInsertedContent sets the InsertedContent +func (r *Replacement) WithInsertedContent(artifactContent *ArtifactContent) *Replacement { + r.InsertedContent = artifactContent + return r +} diff --git a/v2/sarif/replacement_test.go b/v2/sarif/replacement_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/replacement_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/reporting_configuration.go b/v2/sarif/reporting_configuration.go new file mode 100644 index 0000000..4561269 --- /dev/null +++ b/v2/sarif/reporting_configuration.go @@ -0,0 +1,39 @@ +package sarif + +// ReportingConfiguration ... +type ReportingConfiguration struct { + PropertyBag + Enabled *bool `json:"enabled,omitempty"` + Level string `json:"level,omitempty"` + Parameters *PropertyBag `json:"parameters,omitempty"` + Rank *float64 `json:"rank,omitempty"` +} + +// NewReportingConfiguration creates a new ReportingConfiguration and returns a pointer to it +func NewReportingConfiguration() *ReportingConfiguration { + return &ReportingConfiguration{} +} + +// WithEnabled sets the Enabled +func (reportingConfiguration *ReportingConfiguration) WithEnabled(enabled bool) *ReportingConfiguration { + reportingConfiguration.Enabled = &enabled + return reportingConfiguration +} + +// WithLevel sets the Level +func (reportingConfiguration *ReportingConfiguration) WithLevel(level string) *ReportingConfiguration { + reportingConfiguration.Level = level + return reportingConfiguration +} + +// WithParameters sets the Parameters +func (reportingConfiguration *ReportingConfiguration) WithParameters(parameters *PropertyBag) *ReportingConfiguration { + reportingConfiguration.Parameters = parameters + return reportingConfiguration +} + +// WithRank sets the Rank +func (reportingConfiguration *ReportingConfiguration) WithRank(rank float64) *ReportingConfiguration { + reportingConfiguration.Rank = &rank + return reportingConfiguration +} diff --git a/v2/sarif/reporting_descriptor.go b/v2/sarif/reporting_descriptor.go new file mode 100644 index 0000000..8c43fab --- /dev/null +++ b/v2/sarif/reporting_descriptor.go @@ -0,0 +1,92 @@ +package sarif + +// ReportingDescriptor specifies a Sarif ReportingDescriptor object +type ReportingDescriptor struct { + PropertyBag + ID string `json:"id"` + Name *string `json:"name,omitempty"` + ShortDescription *MultiformatMessageString `json:"shortDescription"` + FullDescription *MultiformatMessageString `json:"fullDescription,omitempty"` + DefaultConfiguration *ReportingConfiguration `json:"defaultConfiguration,omitempty"` + HelpURI *string `json:"helpUri,omitempty"` + Help *MultiformatMessageString `json:"help,omitempty"` + Properties Properties `json:"properties,omitempty"` +} + +// NewRule creates a new Rule and returns a pointer to it +func NewRule(ruleID string) *ReportingDescriptor { + return &ReportingDescriptor{ + ID: ruleID, + } +} + +// WithName specifies rule name that is understandable to an end user and returns the updated rule. +func (rule *ReportingDescriptor) WithName(name string) *ReportingDescriptor { + rule.Name = &name + return rule +} + +// WithDescription specifies short description for a rule and returns the updated rule. +// Short description should be a single sentence that is understandable when visible space is limited to a single line +// of text. +func (rule *ReportingDescriptor) WithDescription(description string) *ReportingDescriptor { + rule.ShortDescription = NewMultiformatMessageString(description) + return rule +} + +// WithShortDescription specifies short description for a rule and returns the updated rule. +// Short description should be a single sentence that is understandable when visible space is limited to a single line +// of text. +func (rule *ReportingDescriptor) WithShortDescription(description *MultiformatMessageString) *ReportingDescriptor { + rule.ShortDescription = description + return rule +} + +// WithFullDescription specifies full description for a rule and returns the updated rule. +// Full description should, as far as possible, provide details sufficient to enable resolution of any problem indicated +// by the result. +func (rule *ReportingDescriptor) WithFullDescription(description *MultiformatMessageString) *ReportingDescriptor { + rule.FullDescription = description + return rule +} + +// WithHelpURI specifies a helpURI for a rule and returns the updated rule +func (rule *ReportingDescriptor) WithHelpURI(helpURI string) *ReportingDescriptor { + rule.HelpURI = &helpURI + return rule +} + +// WithHelp sets the rule Help to the provided multiformat message +func (rule *ReportingDescriptor) WithHelp(help *MultiformatMessageString) *ReportingDescriptor { + rule.Help = help + return rule +} + +// WithTextHelp specifies a help text for a rule and returns the updated rule +func (rule *ReportingDescriptor) WithTextHelp(text string) *ReportingDescriptor { + if rule.Help == nil { + rule.Help = &MultiformatMessageString{} + } + rule.Help.Text = &text + return rule +} + +// WithMarkdownHelp specifies a help text for a rule and returns the updated rule +func (rule *ReportingDescriptor) WithMarkdownHelp(markdown string) *ReportingDescriptor { + if rule.Help == nil { + rule.Help = &MultiformatMessageString{} + } + rule.Help.Markdown = &markdown + return rule +} + +// WithProperties specifies properties for a rule and returns the updated rule +func (rule *ReportingDescriptor) WithProperties(properties Properties) *ReportingDescriptor { + rule.Properties = properties + return rule +} + +// AttachPropertyBag adds a property bag to a rule +func (rule *ReportingDescriptor) AttachPropertyBag(pb *PropertyBag) { + rule.Properties = pb.Properties +} diff --git a/v2/sarif/reporting_descriptor_reference.go b/v2/sarif/reporting_descriptor_reference.go new file mode 100644 index 0000000..9b0e61e --- /dev/null +++ b/v2/sarif/reporting_descriptor_reference.go @@ -0,0 +1,40 @@ +package sarif + +// ReportingDescriptorReference ... +type ReportingDescriptorReference struct { + PropertyBag + Id *string `json:"id,omitempty"` + Index *uint `json:"index,omitempty"` + Guid *string `json:"guid,omitempty"` + ToolComponent *ToolComponentReference `json:"toolComponent,omitempty"` +} + +// NewReportingDescriptorReference creates a new ReportingDescriptorReference and returns a pointer to it +func NewReportingDescriptorReference() *ReportingDescriptorReference { + return &ReportingDescriptorReference{} +} + +// WithId sets the Id +func (r *ReportingDescriptorReference) WithId(id string) *ReportingDescriptorReference { + r.Id = &id + return r +} + +// WithIndex sets the Index +func (r *ReportingDescriptorReference) WithIndex(index int) *ReportingDescriptorReference { + i := uint(index) + r.Index = &i + return r +} + +// WithGuid sets the Guid +func (r *ReportingDescriptorReference) WithGuid(guid string) *ReportingDescriptorReference { + r.Guid = &guid + return r +} + +// WithToolComponentReference sets the ToolComponentReference +func (r *ReportingDescriptorReference) WithToolComponentReference(toolComponentRef *ToolComponentReference) *ReportingDescriptorReference { + r.ToolComponent = toolComponentRef + return r +} diff --git a/v2/sarif/reporting_descriptor_reference_test.go b/v2/sarif/reporting_descriptor_reference_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/reporting_descriptor_reference_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/result.go b/v2/sarif/result.go new file mode 100644 index 0000000..4011c63 --- /dev/null +++ b/v2/sarif/result.go @@ -0,0 +1,267 @@ +package sarif + +// Result represents the results block in the sarif report +type Result struct { + PropertyBag + Guid *string `json:"guid,omitempty"` + CorrelationGuid *string `json:"correlationGuid,omitempty"` + RuleID *string `json:"ruleId,omitempty"` + RuleIndex *uint `json:"ruleIndex,omitempty"` + Rule *ReportingDescriptorReference `json:"rule,omitempty"` + Taxa []*ReportingDescriptorReference `json:"taxa,omitempty"` + Kind *string `json:"kind,omitempty"` + Level *string `json:"level,omitempty"` + Message Message `json:"message"` + Locations []*Location `json:"locations,omitempty"` + AnalysisTarget *ArtifactLocation `json:"analysisTarget,omitempty"` + WebRequest *WebRequest `json:"webRequest,omitempty"` + WebResponse *WebResponse `json:"webResponse,omitempty"` + Fingerprints map[string]interface{} `json:"fingerprints,omitempty"` + PartialFingerprints map[string]interface{} `json:"partialFingerprints,omitempty"` + CodeFlows []*CodeFlow `json:"codeFlows,omitempty"` + Graphs []*Graph `json:"graphs,omitempty"` + GraphTraversals []*GraphTraversal `json:"graphTraversals,omitempty"` + Stacks []*Stack `json:"stacks,omitempty"` + RelatedLocations []*Location `json:"relatedLocations,omitempty"` + Suppressions []*Suppression `json:"suppressions,omitempty"` + BaselineState *string `json:"baselineState,omitempty"` + Rank *float32 `json:"rank,omitempty"` + Attachments []*Attachment `json:"attachments,omitempty"` + WorkItemUris []string `json:"workItemUris,omitempty"` // can be null + HostedViewerUri *string `json:"hostedViewerUri,omitempty"` + Provenance *ResultProvenance `json:"provenance,omitempty"` + Fixes []*Fix `json:"fixes,omitempty"` + OccurrenceCount *uint `json:"occurrenceCount,omitempty"` +} + +// NewRuleResult ... +func NewRuleResult(ruleID string) *Result { + return &Result{ + RuleID: &ruleID, + } +} + +// WithGuid sets the Guid +func (result *Result) WithGuid(guid string) *Result { + result.Guid = &guid + return result +} + +// WithCorrelationGuid sets the CorrelationGuid +func (result *Result) WithCorrelationGuid(correlationGuid string) *Result { + result.CorrelationGuid = &correlationGuid + return result +} + +// WithRuleIndex sets the RuleIndex +func (result *Result) WithRuleIndex(ruleIndex int) *Result { + index := uint(ruleIndex) + result.RuleIndex = &index + return result +} + +// WithRule sets the Rule +func (result *Result) WithRule(rule *ReportingDescriptorReference) *Result { + result.Rule = rule + return result +} + +// WithTaxa sets the Taxa +func (result *Result) WithTaxa(taxa []*ReportingDescriptorReference) *Result { + result.Taxa = taxa + return result +} + +// AddTaxa ... +func (result *Result) AddTaxa(taxa *ReportingDescriptorReference) { + result.Taxa = append(result.Taxa, taxa) +} + +// WithKind sets the Kind +func (result *Result) WithKind(kind string) *Result { + result.Kind = &kind + return result +} + +// WithLevel sets the Level +func (result *Result) WithLevel(level string) *Result { + result.Level = &level + return result +} + +// WithMessage sets the Message +func (result *Result) WithMessage(message *Message) *Result { + result.Message = *message + return result +} + +// WithLocations sets the Locations +func (result *Result) WithLocations(locations []*Location) *Result { + result.Locations = locations + return result +} + +// AddLocation ... +func (result *Result) AddLocation(location *Location) { + result.Locations = append(result.Locations, location) +} + +// WithAnalysisTarget sets the AnalysisTarget +func (result *Result) WithAnalysisTarget(target *ArtifactLocation) *Result { + result.AnalysisTarget = target + return result +} + +// WithFingerPrints sets the FingerPrints +func (result *Result) WithFingerPrints(fingerPrints map[string]interface{}) *Result { + result.Fingerprints = fingerPrints + return result +} + +// SetFingerPrint ... +func (result *Result) SetFingerPrint(name string, value interface{}) { + result.Fingerprints[name] = value +} + +// WithPartialFingerPrints sets the PartialFingerPrints +func (result *Result) WithPartialFingerPrints(fingerPrints map[string]interface{}) *Result { + result.PartialFingerprints = fingerPrints + return result +} + +// SetPartialFingerPrint ... +func (result *Result) SetPartialFingerPrint(name string, value interface{}) { + result.PartialFingerprints[name] = value +} + +// WithCodeFlows sets the CodeFlows +func (result *Result) WithCodeFlows(codeFlows []*CodeFlow) *Result { + result.CodeFlows = codeFlows + return result +} + +// AddCodeFlow ... +func (result *Result) AddCodeFlow(codeFlow *CodeFlow) { + result.CodeFlows = append(result.CodeFlows, codeFlow) + +} + +// WithGraphs sets the Graphs +func (result *Result) WithGraphs(graphs []*Graph) *Result { + result.Graphs = graphs + return result +} + +// AddGraph ... +func (result *Result) AddGraph(graph *Graph) { + result.Graphs = append(result.Graphs, graph) + +} + +// WithGraphTraversal sets the GraphTraversal +func (result *Result) WithGraphTraversal(graphTraversals []*GraphTraversal) *Result { + result.GraphTraversals = graphTraversals + return result +} + +// AddGraphTraversal ... +func (result *Result) AddGraphTraversal(graphTraversal *GraphTraversal) { + result.GraphTraversals = append(result.GraphTraversals, graphTraversal) + +} + +// WithStack sets the Stack +func (result *Result) WithStack(stacks []*Stack) *Result { + result.Stacks = stacks + return result +} + +// AddStack ... +func (result *Result) AddStack(stack *Stack) { + result.Stacks = append(result.Stacks, stack) + +} + +// WithRelatedLocations sets the RelatedLocations +func (result *Result) WithRelatedLocations(locations []*Location) *Result { + result.RelatedLocations = locations + return result +} + +// AddRelatedLocation ... +func (result *Result) AddRelatedLocation(location *Location) *Result { + result.RelatedLocations = append(result.RelatedLocations, location) + return result +} + +// WithSuppression sets the Suppression +func (result *Result) WithSuppression(suppressions []*Suppression) *Result { + result.Suppressions = suppressions + return result +} + +// AddSuppression ... +func (result *Result) AddSuppression(suppression *Suppression) { + result.Suppressions = append(result.Suppressions, suppression) + +} + +// WithBaselineState sets the BaselineState +func (result *Result) WithBaselineState(state string) *Result { + result.BaselineState = &state + return result +} + +// WithRank sets the Rank +func (result *Result) WithRank(rank float32) *Result { + result.Rank = &rank + return result +} + +// WithAttachments sets the Attachments +func (result *Result) WithAttachments(attachments []*Attachment) *Result { + result.Attachments = attachments + return result +} + +// AddAttachment ... +func (result *Result) AddAttachment(attachments *Attachment) { + result.Attachments = append(result.Attachments, attachments) + +} + +// WithWorkItemUris sets the WorkItemUris +func (result *Result) WithWorkItemUris(workItemUris []string) *Result { + result.WorkItemUris = workItemUris + return result +} + +// AddWorkItemUri ... +func (result *Result) AddWorkItemUri(workItemUri string) { + result.WorkItemUris = append(result.WorkItemUris, workItemUri) + +} + +// WithHostedViewerUri sets the HostedViewerUri +func (result *Result) WithHostedViewerUri(hostedViewerUri string) *Result { + result.HostedViewerUri = &hostedViewerUri + return result +} + +// WithFix sets the Fix +func (result *Result) WithFix(fixes []*Fix) *Result { + result.Fixes = fixes + return result +} + +// AddFix ... +func (result *Result) AddFix(fix *Fix) { + result.Fixes = append(result.Fixes, fix) +} + +// WithOccurrenceCount sets the OccurrenceCount +func (result *Result) WithOccurrenceCount(occurrenceCount int) *Result { + count := uint(occurrenceCount) + result.OccurrenceCount = &count + return result +} diff --git a/v2/sarif/result_provenance.go b/v2/sarif/result_provenance.go new file mode 100644 index 0000000..c88a4a2 --- /dev/null +++ b/v2/sarif/result_provenance.go @@ -0,0 +1,60 @@ +package sarif + +import "time" + +// ResultProvenance ... +type ResultProvenance struct { + PropertyBag + ConversionSources []*PhysicalLocation `json:"conversionSources,omitempty"` + FirstDetectionRunGUID *string `json:"firstDetectionRunGuid,omitempty"` + FirstDetectionTimeUTC *time.Time `json:"firstDetectionTimeUtc,omitempty"` + InvocationIndex *int `json:"invocationIndex,omitempty"` + LastDetectionRunGUID *string `json:"lastDetectionRunGuid,omitempty"` + LastDetectionTimeUTC *time.Time `json:"lastDetectionTimeUtc,omitempty"` +} + +// NewResultProvenance creates a new ResultProvenance and returns a pointer to it +func NewResultProvenance() *ResultProvenance { + return &ResultProvenance{} +} + +// WithConversionSources sets the ConversionSources +func (resultProvenance *ResultProvenance) WithConversionSources(conversionSources []*PhysicalLocation) *ResultProvenance { + resultProvenance.ConversionSources = conversionSources + return resultProvenance +} + +// AddConversionSource ... +func (resultProvenance *ResultProvenance) AddConversionSource(conversionSource *PhysicalLocation) { + resultProvenance.ConversionSources = append(resultProvenance.ConversionSources, conversionSource) +} + +// WithFirstDetectionRunGUID sets the FirstDetectionRunGUID +func (resultProvenance *ResultProvenance) WithFirstDetectionRunGUID(firstDetectionRunGUID string) *ResultProvenance { + resultProvenance.FirstDetectionRunGUID = &firstDetectionRunGUID + return resultProvenance +} + +// WithFirstDetectionTimeUTC sets the FirstDetectionTimeUTC +func (resultProvenance *ResultProvenance) WithFirstDetectionTimeUTC(firstDetectionTimeUTC *time.Time) *ResultProvenance { + resultProvenance.FirstDetectionTimeUTC = firstDetectionTimeUTC + return resultProvenance +} + +// WithInvocationIndex sets the InvocationIndex +func (resultProvenance *ResultProvenance) WithInvocationIndex(invocationIndex int) *ResultProvenance { + resultProvenance.InvocationIndex = &invocationIndex + return resultProvenance +} + +// WithLastDetectionRunGUID sets the LastDetectionRunGUID +func (resultProvenance *ResultProvenance) WithLastDetectionRunGUID(lastDetectionRunGUID string) *ResultProvenance { + resultProvenance.LastDetectionRunGUID = &lastDetectionRunGUID + return resultProvenance +} + +// WithLastDetectionTimeUTC sets the LastDetectionTimeUTC +func (resultProvenance *ResultProvenance) WithLastDetectionTimeUTC(lastDetectionTimeUTC *time.Time) *ResultProvenance { + resultProvenance.LastDetectionTimeUTC = lastDetectionTimeUTC + return resultProvenance +} diff --git a/v2/sarif/result_test.go b/v2/sarif/result_test.go new file mode 100644 index 0000000..7d324c1 --- /dev/null +++ b/v2/sarif/result_test.go @@ -0,0 +1 @@ +package sarif diff --git a/v2/sarif/run.go b/v2/sarif/run.go new file mode 100644 index 0000000..f4cb8c0 --- /dev/null +++ b/v2/sarif/run.go @@ -0,0 +1,372 @@ +package sarif + +import ( + "fmt" +) + +// RunOption ... +type RunOption int + +// IncludeEmptyResults ... +const IncludeEmptyResults RunOption = iota + +// Run type represents a run of a tool +type Run struct { + PropertyBag + Results []*Result `json:"results"` + Addresses []*Address `json:"addresses,omitempty"` + Artifacts []*Artifact `json:"artifacts,omitempty"` + AutomationDetails *RunAutomationDetails `json:"automationDetails,omitempty"` + BaselineGUID *string `json:"baselineGuid,omitempty"` + ColumnKind interface{} `json:"columnKind,omitempty"` + Conversion *Conversion `json:"conversion,omitempty"` + DefaultEncoding *string `json:"defaultEncoding,omitempty"` + DefaultSourceLanguage *string `json:"defaultSourceLanguage,omitempty"` + ExternalPropertyFileReferences *ExternalPropertyFileReferences `json:"externalPropertyFileReferences,omitempty"` + Graphs []*Graph `json:"graphs,omitempty"` + Invocations []*Invocation `json:"invocations,omitempty"` + Language *string `json:"language,omitempty"` + LogicalLocations []*LogicalLocation `json:"logicalLocations,omitempty"` + NewlineSequences []string `json:"newlineSequences,omitempty"` + OriginalUriBaseIDs map[string]*ArtifactLocation `json:"originalUriBaseIds,omitempty"` + Policies []*ToolComponent `json:"policies,omitempty"` + RedactionTokens []string `json:"redactionTokens,omitempty"` + RunAggregates []*RunAutomationDetails `json:"runAggregates,omitempty"` + SpecialLocations *SpecialLocations `json:"specialLocations,omitempty"` + Taxonomies []*ToolComponent `json:"taxonomies,omitempty"` + ThreadFlowLocations []*ThreadFlowLocation `json:"threadFlowLocations,omitempty"` + Tool Tool `json:"tool"` + Translations []*ToolComponent `json:"translations,omitempty"` + VersionControlProvenance []*VersionControlDetails `json:"versionControlProvenance,omitempty"` + WebRequests []*WebRequest `json:"webRequests,omitempty"` + WebResponses []*WebResponse `json:"webResponses,omitempty"` +} + +// NewRun creates a new Run and returns a pointer to it +func NewRun(tool Tool) *Run { + return &Run{ + Tool: tool, + } +} + +// NewRunWithInformationURI creates a new Run and returns a pointer to it +func NewRunWithInformationURI(toolName, informationURI string) *Run { + run := &Run{ + Tool: Tool{ + Driver: &ToolComponent{ + Name: toolName, + InformationURI: &informationURI, + }, + }, + Results: []*Result{}, + } + + return run +} + +// WithResults sets the Results +func (run *Run) WithResults(results []*Result) *Run { + run.Results = results + return run +} + +// AddResult ... +func (run *Run) AddResult(result *Result) { + run.Results = append(run.Results, result) +} + +// WithAddresses sets the Addresses +func (run *Run) WithAddresses(addresses []*Address) *Run { + run.Addresses = addresses + return run +} + +// WithArtifacts sets the Artifacts +func (run *Run) WithArtifacts(artifacts []*Artifact) *Run { + run.Artifacts = artifacts + return run +} + +// WithAutomationDetails sets the AutomationDetails +func (run *Run) WithAutomationDetails(automationDetails *RunAutomationDetails) *Run { + run.AutomationDetails = automationDetails + return run +} + +// WithBaselineGUID sets the BaselineGUID +func (run *Run) WithBaselineGUID(baselineGUID string) *Run { + run.BaselineGUID = &baselineGUID + return run +} + +// WithColumnKind sets the ColumnKind +func (run *Run) WithColumnKind(columnKind interface{}) *Run { + run.ColumnKind = columnKind + return run +} + +// WithConversion sets the Conversion +func (run *Run) WithConversion(conversion *Conversion) *Run { + run.Conversion = conversion + return run +} + +// WithDefaultEncoding sets the DefaultEncoding +func (run *Run) WithDefaultEncoding(defaultEncoding string) *Run { + run.DefaultEncoding = &defaultEncoding + return run +} + +// WithDefaultSourceLanguage sets the DefaultSourceLanguage +func (run *Run) WithDefaultSourceLanguage(defaultSourceLangauge string) *Run { + run.DefaultSourceLanguage = &defaultSourceLangauge + return run +} + +// WithExternalPropertyFileReferences sets the ExternalPropertyFileReferences +func (run *Run) WithExternalPropertyFileReferences(references *ExternalPropertyFileReferences) *Run { + run.ExternalPropertyFileReferences = references + return run +} + +// WithGraphs sets the Graphs +func (run *Run) WithGraphs(graphs []*Graph) *Run { + run.Graphs = graphs + return run +} + +// AddGraph ... +func (run *Run) AddGraph(graph *Graph) { + run.Graphs = append(run.Graphs, graph) +} + +// WithInvocations sets the Invocations +func (run *Run) WithInvocations(invocations []*Invocation) *Run { + run.Invocations = invocations + return run +} + +// AddInvocations ... +func (run *Run) AddInvocations(invocation *Invocation) { + run.Invocations = append(run.Invocations, invocation) +} + +// WithLanguage sets the Language +func (run *Run) WithLanguage(language string) *Run { + run.Language = &language + return run +} + +// WithLogicalLocations sets the LogicalLocations +func (run *Run) WithLogicalLocations(locations []*LogicalLocation) *Run { + run.LogicalLocations = locations + return run +} + +// AddILogicalLocation ... +func (run *Run) AddILogicalLocation(logicalLocation *LogicalLocation) { + run.LogicalLocations = append(run.LogicalLocations, logicalLocation) +} + +// WithNewlineSequences sets the NewlineSequences +func (run *Run) WithNewlineSequences(newLines []string) *Run { + run.NewlineSequences = newLines + return run +} + +// WithOriginalUriBaseIds sets the OriginalUriBaseIds +func (run *Run) WithOriginalUriBaseIds(originalUriBaseIDs map[string]*ArtifactLocation) *Run { + run.OriginalUriBaseIDs = originalUriBaseIDs + return run +} + +// WithPolicies sets the Policies +func (run *Run) WithPolicies(policies []*ToolComponent) *Run { + run.Policies = policies + return run +} + +// AddPolicy ... +func (run *Run) AddPolicy(policy *ToolComponent) { + run.Policies = append(run.Policies, policy) +} + +// WithRedactionTokens sets the RedactionTokens +func (run *Run) WithRedactionTokens(redactedTokens []string) *Run { + run.RedactionTokens = redactedTokens + return run +} + +// WithRunAggregates sets the RunAggregates +func (run *Run) WithRunAggregates(runAggregates []*RunAutomationDetails) *Run { + run.RunAggregates = runAggregates + return run +} + +// AddRunAggregate ... +func (run *Run) AddRunAggregate(runAggregate *RunAutomationDetails) { + run.RunAggregates = append(run.RunAggregates, runAggregate) +} + +// WithSpecialLocations sets the SpecialLocations +func (run *Run) WithSpecialLocations(specialLocation *SpecialLocations) *Run { + run.SpecialLocations = specialLocation + return run +} + +// WithTaxonomies sets the Taxonomies +func (run *Run) WithTaxonomies(taxonomies []*ToolComponent) *Run { + run.Taxonomies = taxonomies + return run +} + +// AddTaxonomy ... +func (run *Run) AddTaxonomy(taxonomy *ToolComponent) { + run.Taxonomies = append(run.Taxonomies, taxonomy) +} + +// WithThreadFlowLocations sets the ThreadFlowLocations +func (run *Run) WithThreadFlowLocations(threadFlowLocations []*ThreadFlowLocation) *Run { + run.ThreadFlowLocations = threadFlowLocations + return run +} + +// AddThreadFlowLocation ... +func (run *Run) AddThreadFlowLocation(threadFlowLocation *ThreadFlowLocation) { + run.ThreadFlowLocations = append(run.ThreadFlowLocations, threadFlowLocation) +} + +// WithTranslations sets the Translations +func (run *Run) WithTranslations(translations []*ToolComponent) *Run { + run.Translations = translations + return run +} + +// AddTranslation ... +func (run *Run) AddTranslation(translation *ToolComponent) { + run.Translations = append(run.Translations, translation) +} + +// WithVersionControlProvenances sets the VersionControlProvenances +func (run *Run) WithVersionControlProvenances(vcProvenance []*VersionControlDetails) *Run { + run.VersionControlProvenance = vcProvenance + return run +} + +// AddVersionControlProvenance ... +func (run *Run) AddVersionControlProvenance(vcProvenance *VersionControlDetails) { + run.VersionControlProvenance = append(run.VersionControlProvenance, vcProvenance) +} + +// WithWebRequests sets the WebRequests +func (run *Run) WithWebRequests(webRequests []*WebRequest) *Run { + run.WebRequests = webRequests + return run +} + +// AddWebRequest ... +func (run *Run) AddWebRequest(webRequest *WebRequest) { + run.WebRequests = append(run.WebRequests, webRequest) +} + +// WithWebResponses sets the WebResponses +func (run *Run) WithWebResponses(webResponses []*WebResponse) *Run { + run.WebResponses = webResponses + return run +} + +// AddWebResponse ... +func (run *Run) AddWebResponse(webResponse *WebResponse) { + run.WebResponses = append(run.WebResponses, webResponse) +} + +// AddInvocation adds an invocation to the run and returns a pointer to it +func (run *Run) AddInvocation(executionSuccessful bool) *Invocation { + i := &Invocation{ + ExecutionSuccessful: &executionSuccessful, + } + run.Invocations = append(run.Invocations, i) + return i +} + +// AddArtifact adds an artifact to the run and returns a pointer to it +func (run *Run) AddArtifact() *Artifact { + a := &Artifact{ + Length: -1, + } + run.Artifacts = append(run.Artifacts, a) + return a +} + +// AddDistinctArtifact will handle deduplication of simple artifact additions +func (run *Run) AddDistinctArtifact(uri string) *Artifact { + for _, artifact := range run.Artifacts { + if *artifact.Location.URI == uri { + return artifact + } + } + + a := &Artifact{ + Length: -1, + } + a.WithLocation(NewSimpleArtifactLocation(uri)) + + run.Artifacts = append(run.Artifacts, a) + return a +} + +// AddRule returns an existing ReportingDescriptor for the ruleID or creates a new ReportingDescriptor and returns a pointer to it +func (run *Run) AddRule(ruleID string) *ReportingDescriptor { + for _, rule := range run.Tool.Driver.Rules { + if rule.ID == ruleID { + return rule + } + } + rule := NewRule(ruleID) + run.Tool.Driver.Rules = append(run.Tool.Driver.Rules, rule) + return rule +} + +// CreateResultForRule returns an existing Result or creates a new one and returns a pointer to it +func (run *Run) CreateResultForRule(ruleID string) *Result { + result := NewRuleResult(ruleID) + run.Results = append(run.Results, result) + return result +} + +// GetRuleById finds a rule by a given rule ID and returns a pointer to it +func (run *Run) GetRuleById(ruleId string) (*ReportingDescriptor, error) { + if run.Tool.Driver != nil { + for _, rule := range run.Tool.Driver.Rules { + if rule.ID == ruleId { + return rule, nil + } + } + } + return nil, fmt.Errorf("couldn't find rule %s", ruleId) +} + +// GetResultByRuleId finds the result for a ruleId and returns a pointer to it +func (run *Run) GetResultByRuleId(ruleId string) (*Result, error) { + for _, result := range run.Results { + if *result.RuleID == ruleId { + return result, nil + } + } + return nil, fmt.Errorf("couldn't find a result for rule %s", ruleId) +} + +// DedupeArtifacts ... +func (run *Run) DedupeArtifacts() error { + dupes := map[*Artifact]bool{} + deduped := []*Artifact{} + + for _, a := range run.Artifacts { + if _, ok := dupes[a]; !ok { + dupes[a] = true + deduped = append(deduped, a) + } + } + run.Artifacts = deduped + return nil +} diff --git a/v2/sarif/run_automation_details.go b/v2/sarif/run_automation_details.go new file mode 100644 index 0000000..bff0943 --- /dev/null +++ b/v2/sarif/run_automation_details.go @@ -0,0 +1,57 @@ +package sarif + +// RunAutomationDetails ... +type RunAutomationDetails struct { + PropertyBag + CorrelationGUID *string `json:"correlationGuid,omitempty"` + Description *Message `json:"description,omitempty"` + GUID *string `json:"guid,omitempty"` + ID *string `json:"id,omitempty"` +} + +// NewRunAutomationDetails ... +func NewRunAutomationDetails() *RunAutomationDetails { + return &RunAutomationDetails{} +} + +// WithCorrelationGUID sets the CorrelationGUID +func (runAutomationDetails *RunAutomationDetails) WithCorrelationGUID(correlationGUID string) *RunAutomationDetails { + runAutomationDetails.CorrelationGUID = &correlationGUID + return runAutomationDetails +} + +// WithDescription sets the Message +func (runAutomationDetails *RunAutomationDetails) WithDescription(description *Message) *RunAutomationDetails { + runAutomationDetails.Description = description + return runAutomationDetails +} + +// WithDescriptionText sets the Message text +func (runAutomationDetails *RunAutomationDetails) WithDescriptionText(text string) *RunAutomationDetails { + if runAutomationDetails.Description == nil { + runAutomationDetails.Description = &Message{} + } + runAutomationDetails.Description.Text = &text + return runAutomationDetails +} + +// WithDescriptionMarkdown sets the Message markdown +func (runAutomationDetails *RunAutomationDetails) WithDescriptionMarkdown(markdown string) *RunAutomationDetails { + if runAutomationDetails.Description == nil { + runAutomationDetails.Description = &Message{} + } + runAutomationDetails.Description.Markdown = &markdown + return runAutomationDetails +} + +// WithGUID sets the GUID +func (runAutomationDetails *RunAutomationDetails) WithGUID(guid string) *RunAutomationDetails { + runAutomationDetails.GUID = &guid + return runAutomationDetails +} + +// WithID sets the ID +func (runAutomationDetails *RunAutomationDetails) WithID(id string) *RunAutomationDetails { + runAutomationDetails.ID = &id + return runAutomationDetails +} diff --git a/v2/sarif/sarif.go b/v2/sarif/sarif.go new file mode 100644 index 0000000..b9a0c16 --- /dev/null +++ b/v2/sarif/sarif.go @@ -0,0 +1,115 @@ +package sarif + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" +) + +// Version is the version of Sarif to use +type Version string + +// Version210 represents Version210 of Sarif +const Version210 Version = "2.1.0" + +var versions = map[Version]string{ + Version210: "https://json.schemastore.org/sarif-2.1.0-rtm.5.json", +} + +// Report is the encapsulating type representing a Sarif Report +type Report struct { + PropertyBag + InlineExternalProperties []*ExternalProperties `json:"inlineExternalProperties,omitempty"` + Version string `json:"version"` + Schema string `json:"$schema,omitempty"` + Runs []*Run `json:"runs"` +} + +// New Creates a new Report or returns an error +func New(version Version) (*Report, error) { + schema, err := getVersionSchema(version) + if err != nil { + return nil, err + } + return &Report{ + Version: string(version), + Schema: schema, + Runs: []*Run{}, + }, nil +} + +// Open loads a Report from a file +func Open(filename string) (*Report, error) { + if _, err := os.Stat(filename); err != nil && os.IsNotExist(err) { + return nil, fmt.Errorf("the provided file path doesn't have a file") + } + + content, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("the provided filepath could not be opened. %w", err) + } + return FromBytes(content) +} + +// FromString loads a Report from string content +func FromString(content string) (*Report, error) { + return FromBytes([]byte(content)) +} + +// FromBytes loads a Report from a byte array +func FromBytes(content []byte) (*Report, error) { + var report Report + if err := json.Unmarshal(content, &report); err != nil { + return nil, err + } + return &report, nil +} + +// AddRun allows adding run information to the current report +func (sarif *Report) AddRun(run *Run) { + sarif.Runs = append(sarif.Runs, run) +} + +func getVersionSchema(version Version) (string, error) { + for ver, schema := range versions { + if ver == version { + return schema, nil + } + } + 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|os.O_WRONLY, os.ModeAppend) + if err != nil { + return err + } + defer func() { _ = file.Close() }() + 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 { + run.DedupeArtifacts() + } + marshal, err := json.Marshal(sarif) + if err != nil { + return err + } + _, err = w.Write(marshal) + return err +} + +// PrettyWrite writes the JSON output with indentation +func (sarif *Report) PrettyWrite(w io.Writer) error { + marshal, err := json.MarshalIndent(sarif, "", " ") + if err != nil { + return err + } + _, err = w.Write(marshal) + return err +} diff --git a/v2/sarif/special_locations.go b/v2/sarif/special_locations.go new file mode 100644 index 0000000..73a92e6 --- /dev/null +++ b/v2/sarif/special_locations.go @@ -0,0 +1,18 @@ +package sarif + +// SpecialLocations ... +type SpecialLocations struct { + PropertyBag + DisplayBase *ArtifactLocation `json:"displayBase,omitempty"` +} + +// NewSpecialLocations ... +func NewSpecialLocations() *SpecialLocations { + return &SpecialLocations{} +} + +// WithDisplayBase sets the DisplayBase +func (specialLocations *SpecialLocations) WithDisplayBase(displayBase *ArtifactLocation) *SpecialLocations { + specialLocations.DisplayBase = displayBase + return specialLocations +} diff --git a/v2/sarif/stack.go b/v2/sarif/stack.go new file mode 100644 index 0000000..20f7c7b --- /dev/null +++ b/v2/sarif/stack.go @@ -0,0 +1,49 @@ +package sarif + +// Stack ... +type Stack struct { + PropertyBag + Frames []*StackFrame `json:"frames"` + Message *Message `json:"message,omitempty"` + Properties *PropertyBag `json:"properties,omitempty"` +} + +// NewStack creates a new Stack and returns a pointer to it +func NewStack() *Stack { + return &Stack{} +} + +// WithFrames sets the Frames +func (stack *Stack) WithFrames(frames []*StackFrame) *Stack { + stack.Frames = frames + return stack +} + +// AddFrame ... +func (stack *Stack) AddFrame(frame *StackFrame) { + stack.Frames = append(stack.Frames, frame) +} + +// WithMessage sets the Message +func (stack *Stack) WithMessage(message *Message) *Stack { + stack.Message = message + return stack +} + +// WithTextMessage sets the Message text +func (stack *Stack) WithTextMessage(text string) *Stack { + if stack.Message == nil { + stack.Message = &Message{} + } + stack.Message.Text = &text + return stack +} + +// WithMessageMarkdown sets the Message markdown +func (stack *Stack) WithMessageMarkdown(markdown string) *Stack { + if stack.Message == nil { + stack.Message = &Message{} + } + stack.Message.Markdown = &markdown + return stack +} diff --git a/v2/sarif/stack_frame.go b/v2/sarif/stack_frame.go new file mode 100644 index 0000000..b1bda53 --- /dev/null +++ b/v2/sarif/stack_frame.go @@ -0,0 +1,44 @@ +package sarif + +// StackFrame ... +type StackFrame struct { + PropertyBag + Location *Location `json:"location,omitempty"` + Module *string `json:"module,omitempty"` + Parameters []string `json:"parameters,omitempty"` + ThreadID *int `json:"threadId,omitempty"` +} + +// NewStackFrame creates a new StackFrame and returns a pointer to it +func NewStackFrame() *StackFrame { + return &StackFrame{} +} + +// WithLocation sets the Location +func (stackFrame *StackFrame) WithLocation(location *Location) *StackFrame { + stackFrame.Location = location + return stackFrame +} + +// WithModule sets the Module +func (stackFrame *StackFrame) WithModule(module string) *StackFrame { + stackFrame.Module = &module + return stackFrame +} + +// WithParameters sets the Parameters +func (stackFrame *StackFrame) WithParameters(parameters []string) *StackFrame { + stackFrame.Parameters = parameters + return stackFrame +} + +// AddParameter ... +func (stackFrame *StackFrame) AddParameter(parameter string) { + stackFrame.Parameters = append(stackFrame.Parameters, parameter) +} + +// WithThreadID sets the ThreadID +func (stackFrame *StackFrame) WithThreadID(threadID int) *StackFrame { + stackFrame.ThreadID = &threadID + return stackFrame +} diff --git a/v2/sarif/suppression.go b/v2/sarif/suppression.go new file mode 100644 index 0000000..68c3a50 --- /dev/null +++ b/v2/sarif/suppression.go @@ -0,0 +1,42 @@ +package sarif + +// Suppression ... +type Suppression struct { + PropertyBag + Kind string `json:"kind"` + Status *string `json:"status"` + Location *Location `json:"location"` + Guid *string `json:"guid"` + Justification *string `json:"justification"` +} + +// NewSuppression creates a new Suppression and returns a pointer to it +func NewSuppression(kind string) *Suppression { + return &Suppression{ + Kind: kind, + } +} + +// WithStatus sets the Status +func (s *Suppression) WithStatus(status string) *Suppression { + s.Status = &status + return s +} + +// WithLocation sets the Location +func (s *Suppression) WithLocation(location *Location) *Suppression { + s.Location = location + return s +} + +// WithGuid sets the Guid +func (s *Suppression) WithGuid(guid string) *Suppression { + s.Guid = &guid + return s +} + +// WithJustifcation sets the Justifcation +func (s *Suppression) WithJustifcation(justification string) *Suppression { + s.Justification = &justification + return s +} diff --git a/v2/sarif/test_helpers.go b/v2/sarif/test_helpers.go new file mode 100644 index 0000000..a888f44 --- /dev/null +++ b/v2/sarif/test_helpers.go @@ -0,0 +1,11 @@ +package sarif + +import "encoding/json" + +func getJsonString(value interface{}) string { + j, err := json.Marshal(value) + if err != nil { + panic(err) + } + return string(j) +} diff --git a/v2/sarif/thread_flow.go b/v2/sarif/thread_flow.go new file mode 100644 index 0000000..f51bb49 --- /dev/null +++ b/v2/sarif/thread_flow.go @@ -0,0 +1,69 @@ +package sarif + +// ThreadFlow ... +type ThreadFlow struct { + PropertyBag + ID *string `json:"id,omitempty"` + ImmutableState map[string]*MultiformatMessageString `json:"immutableState,omitempty"` + InitialState map[string]*MultiformatMessageString `json:"initialState,omitempty"` + Locations []*ThreadFlowLocation `json:"locations"` + Message *Message `json:"message,omitempty"` +} + +// NewThreadFlow creates a new ThreadFlow and returns a pointer to it +func NewThreadFlow() *ThreadFlow { + return &ThreadFlow{} +} + +// WithID sets the ID +func (threadFlow *ThreadFlow) WithID(id string) *ThreadFlow { + threadFlow.ID = &id + return threadFlow +} + +// WithImmutableState sets the ImmutableState +func (threadFlow *ThreadFlow) WithImmutableState(immutableState map[string]*MultiformatMessageString) *ThreadFlow { + threadFlow.ImmutableState = immutableState + return threadFlow +} + +// WithInitialState sets the InitialState +func (threadFlow *ThreadFlow) WithInitialState(initialState map[string]*MultiformatMessageString) *ThreadFlow { + threadFlow.InitialState = initialState + return threadFlow +} + +// WithLocations sets the Locations +func (threadFlow *ThreadFlow) WithLocations(locations []*ThreadFlowLocation) *ThreadFlow { + threadFlow.Locations = locations + return threadFlow +} + +// AddLocation ... +func (threadFlow *ThreadFlow) AddLocation(location *ThreadFlowLocation) { + threadFlow.Locations = append(threadFlow.Locations, location) +} + +// WithMessage sets the Message +func (threadFlow *ThreadFlow) WithMessage(message *Message) *ThreadFlow { + threadFlow.Message = message + return threadFlow +} + +// WithTextMessage sets the Message text +func (threadFlow *ThreadFlow) WithTextMessage(text string) *ThreadFlow { + if threadFlow.Message == nil { + threadFlow.Message = &Message{} + } + threadFlow.Message.Text = &text + return threadFlow +} + +// WithMessageMarkdown sets the Message markdown +func (threadFlow *ThreadFlow) WithMessageMarkdown(markdown string) *ThreadFlow { + if threadFlow.Message == nil { + threadFlow.Message = &Message{} + } + threadFlow.Message.Markdown = &markdown + return threadFlow +} diff --git a/v2/sarif/thread_flow_location.go b/v2/sarif/thread_flow_location.go new file mode 100644 index 0000000..1b7d27e --- /dev/null +++ b/v2/sarif/thread_flow_location.go @@ -0,0 +1,114 @@ +package sarif + +import "time" + +// ThreadFlowLocation ... +type ThreadFlowLocation struct { + PropertyBag + ExecutionOrder *int `json:"executionOrder,omitempty"` + ExecutionTimeUTC *time.Time `json:"executionTimeUtc,omitempty"` + Importance interface{} `json:"importance,omitempty"` + Index *int `json:"index,omitempty"` + Kinds []string `json:"kinds,omitempty"` + Location *Location `json:"location,omitempty"` + Module *string `json:"module,omitempty"` + NestingLevel *int `json:"nestingLevel,omitempty"` + Stack *Stack `json:"stack,omitempty"` + State map[string]*MultiformatMessageString `json:"state,omitempty"` + Taxa []*ReportingDescriptorReference `json:"taxa,omitempty"` + WebRequest *WebRequest `json:"webRequest,omitempty"` + WebResponse *WebResponse `json:"webResponse,omitempty"` +} + +// NewThreadFlowLocation creates a new ThreadFlowLocation and returns a pointer to it +func NewThreadFlowLocation() *ThreadFlowLocation { + return &ThreadFlowLocation{} +} + +// WithExecutionOrder sets the ExecutionOrder +func (threadFlowLocation *ThreadFlowLocation) WithExecutionOrder(order int) *ThreadFlowLocation { + threadFlowLocation.ExecutionOrder = &order + return threadFlowLocation +} + +// WithExecutionTimeUTC sets the ExecutionTimeUTC +func (threadFlowLocation *ThreadFlowLocation) WithExecutionTimeUTC(executionTimeUTC *time.Time) *ThreadFlowLocation { + threadFlowLocation.ExecutionTimeUTC = executionTimeUTC + return threadFlowLocation +} + +// WithImportance sets the Importance +func (threadFlowLocation *ThreadFlowLocation) WithImportance(importance interface{}) *ThreadFlowLocation { + threadFlowLocation.Importance = importance + return threadFlowLocation +} + +// WithIndex sets the Index +func (threadFlowLocation *ThreadFlowLocation) WithIndex(index int) *ThreadFlowLocation { + threadFlowLocation.Index = &index + return threadFlowLocation +} + +// WithKinds sets the Kinds +func (threadFlowLocation *ThreadFlowLocation) WithKinds(kinds []string) *ThreadFlowLocation { + threadFlowLocation.Kinds = kinds + return threadFlowLocation +} + +// AddKind ... +func (threadFlowLocation *ThreadFlowLocation) AddKind(kind string) { + threadFlowLocation.Kinds = append(threadFlowLocation.Kinds, kind) +} + +// WithLocation sets the Location +func (threadFlowLocation *ThreadFlowLocation) WithLocation(location *Location) *ThreadFlowLocation { + threadFlowLocation.Location = location + return threadFlowLocation +} + +// WithModule sets the Module +func (threadFlowLocation *ThreadFlowLocation) WithModule(module string) *ThreadFlowLocation { + threadFlowLocation.Module = &module + return threadFlowLocation +} + +// WithNestingLevel sets the NestingLevel +func (threadFlowLocation *ThreadFlowLocation) WithNestingLevel(nestingLevel int) *ThreadFlowLocation { + threadFlowLocation.NestingLevel = &nestingLevel + return threadFlowLocation +} + +// WithStack sets the Stack +func (threadFlowLocation *ThreadFlowLocation) WithStack(stack *Stack) *ThreadFlowLocation { + threadFlowLocation.Stack = stack + return threadFlowLocation +} + +// WithState sets the State +func (threadFlowLocation *ThreadFlowLocation) WithState(state map[string]*MultiformatMessageString) *ThreadFlowLocation { + threadFlowLocation.State = state + return threadFlowLocation +} + +// WithTaxa sets the Taxa +func (threadFlowLocation *ThreadFlowLocation) WithTaxa(taxa []*ReportingDescriptorReference) *ThreadFlowLocation { + threadFlowLocation.Taxa = taxa + return threadFlowLocation +} + +// AddTaxa ... +func (threadFlowLocation *ThreadFlowLocation) AddTaxa(taxa *ReportingDescriptorReference) { + threadFlowLocation.Taxa = append(threadFlowLocation.Taxa, taxa) +} + +// WithWebRequest sets the WebRequest +func (threadFlowLocation *ThreadFlowLocation) WithWebRequest(webRequest *WebRequest) *ThreadFlowLocation { + threadFlowLocation.WebRequest = webRequest + return threadFlowLocation +} + +// WithWebResponse sets the WebResponse +func (threadFlowLocation *ThreadFlowLocation) WithWebResponse(webResponse *WebResponse) *ThreadFlowLocation { + threadFlowLocation.WebResponse = webResponse + return threadFlowLocation +} diff --git a/v2/sarif/tool.go b/v2/sarif/tool.go new file mode 100644 index 0000000..b6be259 --- /dev/null +++ b/v2/sarif/tool.go @@ -0,0 +1,21 @@ +package sarif + +// Tool ... +type Tool struct { + PropertyBag + Driver *ToolComponent `json:"driver"` +} + +// NewTool creates a new Tool and returns a pointer to it +func NewTool(driver *ToolComponent) *Tool { + return &Tool{ + Driver: driver, + } +} + +// NewSimpleTool creates a new SimpleTool and returns a pointer to it +func NewSimpleTool(driverName string) *Tool { + return &Tool{ + Driver: NewDriver(driverName), + } +} diff --git a/v2/sarif/tool_component.go b/v2/sarif/tool_component.go new file mode 100644 index 0000000..15a6aa2 --- /dev/null +++ b/v2/sarif/tool_component.go @@ -0,0 +1,82 @@ +package sarif + +// ToolComponent ... +type ToolComponent struct { + PropertyBag + Name string `json:"name"` + Version *string `json:"version,omitempty"` + InformationURI *string `json:"informationUri"` + Notifications []*ReportingDescriptor `json:"notifications,omitempty"` + Rules []*ReportingDescriptor `json:"rules,omitempty"` + Taxa []*ReportingDescriptor `json:"taxa,omitempty"` +} + +// NewDriver creates a new Driver and returns a pointer to it +func NewDriver(name string) *ToolComponent { + return &ToolComponent{ + Name: name, + } +} + +// NewVersionedDriver creates a new VersionedDriver and returns a pointer to it +func NewVersionedDriver(name, version string) *ToolComponent { + return &ToolComponent{ + Name: name, + Version: &version, + } +} + +// WithVersion specifies tool version, in whatever format it natively provides. Returns updated driver. +func (driver *ToolComponent) WithVersion(version string) *ToolComponent { + driver.Version = &version + return driver +} + +func (driver *ToolComponent) getOrCreateRule(rule *ReportingDescriptor) uint { + for i, r := range driver.Rules { + if r.ID == rule.ID { + return uint(i) + } + } + driver.Rules = append(driver.Rules, rule) + return uint(len(driver.Rules) - 1) +} + +// WithInformationURI sets the InformationURI +func (driver *ToolComponent) WithInformationURI(informationURI string) *ToolComponent { + driver.InformationURI = &informationURI + return driver +} + +// WithNotifications sets the Notifications +func (driver *ToolComponent) WithNotifications(notifications []*ReportingDescriptor) *ToolComponent { + driver.Notifications = notifications + return driver +} + +// AddNotification ... +func (driver *ToolComponent) AddNotification(notification *ReportingDescriptor) { + driver.Notifications = append(driver.Notifications, notification) +} + +// WithNRules sets the NRules +func (driver *ToolComponent) WithNRules(rules []*ReportingDescriptor) *ToolComponent { + driver.Rules = rules + return driver +} + +// AddRule ... +func (driver *ToolComponent) AddRule(rule *ReportingDescriptor) { + driver.Rules = append(driver.Rules, rule) +} + +// WithTaxa sets the Taxa +func (driver *ToolComponent) WithTaxa(taxa []*ReportingDescriptor) *ToolComponent { + driver.Taxa = taxa + return driver +} + +// AddTaxa ... +func (driver *ToolComponent) AddTaxa(taxa *ReportingDescriptor) { + driver.Taxa = append(driver.Taxa, taxa) +} diff --git a/v2/sarif/tool_component_reference.go b/v2/sarif/tool_component_reference.go new file mode 100644 index 0000000..8ef3b2e --- /dev/null +++ b/v2/sarif/tool_component_reference.go @@ -0,0 +1,33 @@ +package sarif + +// ToolComponentReference ... +type ToolComponentReference struct { + PropertyBag + Name *string `json:"name"` + Index *uint `json:"index"` + Guid *string `json:"guid"` +} + +// NewToolComponentReference creates a new ToolComponentReference and returns a pointer to it +func NewToolComponentReference() *ToolComponentReference { + return &ToolComponentReference{} +} + +// WithName sets the Name +func (t *ToolComponentReference) WithName(name string) *ToolComponentReference { + t.Name = &name + return t +} + +// WithIndex sets the Index +func (t *ToolComponentReference) WithIndex(index int) *ToolComponentReference { + i := uint(index) + t.Index = &i + return t +} + +// WithGuid sets the Guid +func (t *ToolComponentReference) WithGuid(guid string) *ToolComponentReference { + t.Guid = &guid + return t +} diff --git a/v2/sarif/translation_metadata.go b/v2/sarif/translation_metadata.go new file mode 100644 index 0000000..c304be5 --- /dev/null +++ b/v2/sarif/translation_metadata.go @@ -0,0 +1,90 @@ +package sarif + +// TranslationMetadata ... +type TranslationMetadata struct { + PropertyBag + DownloadURI *string `json:"downloadUri,omitempty"` + FullDescription *MultiformatMessageString `json:"fullDescription,omitempty"` + FullName *string `json:"fullName,omitempty"` + InformationURI *string `json:"informationUri,omitempty"` + Name *string `json:"name"` + ShortDescription *MultiformatMessageString `json:"shortDescription,omitempty"` +} + +// NewTranslationMetadata creates a new TranslationMetadata and returns a pointer to it +func NewTranslationMetadata() *TranslationMetadata { + return &TranslationMetadata{} +} + +// WithDownloadURI sets the DownloadURI +func (translationMetadata *TranslationMetadata) WithDownloadURI(downloadURI string) *TranslationMetadata { + translationMetadata.DownloadURI = &downloadURI + return translationMetadata +} + +// WithFullDescription sets the FullDescription +func (translationMetadata *TranslationMetadata) WithFullDescription(message *MultiformatMessageString) *TranslationMetadata { + translationMetadata.FullDescription = message + return translationMetadata +} + +// WithFullDescriptionText sets the FullDescriptionText +func (translationMetadata *TranslationMetadata) WithFullDescriptionText(text string) *TranslationMetadata { + if translationMetadata.FullDescription == nil { + translationMetadata.FullDescription = &MultiformatMessageString{} + } + translationMetadata.FullDescription.Text = &text + return translationMetadata +} + +// WithFullDescriptionMarkdown sets the FullDescriptionMarkdown +func (translationMetadata *TranslationMetadata) WithFullDescriptionMarkdown(markdown string) *TranslationMetadata { + if translationMetadata.FullDescription == nil { + translationMetadata.FullDescription = &MultiformatMessageString{} + } + translationMetadata.FullDescription.Markdown = &markdown + return translationMetadata +} + +// WithFullName sets the FullName +func (translationMetadata *TranslationMetadata) WithFullName(fullname string) *TranslationMetadata { + translationMetadata.FullName = &fullname + return translationMetadata +} + +// WithInformationURI sets the InformationURI +func (translationMetadata *TranslationMetadata) WithInformationURI(informationURI string) *TranslationMetadata { + translationMetadata.InformationURI = &informationURI + return translationMetadata +} + +// WithName sets the Name +func (translationMetadata *TranslationMetadata) WithName(name string) *TranslationMetadata { + translationMetadata.Name = &name + + return translationMetadata +} + +// WithShortDescription sets the ShortDescription +func (translationMetadata *TranslationMetadata) WithShortDescription(message *MultiformatMessageString) *TranslationMetadata { + translationMetadata.ShortDescription = message + return translationMetadata +} + +// WithShortShortDescriptionText sets the ShortShortDescriptionText +func (translationMetadata *TranslationMetadata) WithShortShortDescriptionText(text string) *TranslationMetadata { + if translationMetadata.ShortDescription == nil { + translationMetadata.ShortDescription = &MultiformatMessageString{} + } + translationMetadata.ShortDescription.Text = &text + return translationMetadata +} + +// WithShortDescriptionMarkdown sets the ShortDescriptionMarkdown +func (translationMetadata *TranslationMetadata) WithShortDescriptionMarkdown(markdown string) *TranslationMetadata { + if translationMetadata.ShortDescription == nil { + translationMetadata.ShortDescription = &MultiformatMessageString{} + } + translationMetadata.ShortDescription.Markdown = &markdown + return translationMetadata +} diff --git a/v2/sarif/version_control_details.go b/v2/sarif/version_control_details.go new file mode 100644 index 0000000..4dbed9e --- /dev/null +++ b/v2/sarif/version_control_details.go @@ -0,0 +1,55 @@ +package sarif + +import "time" + +// VersionControlDetails ... +type VersionControlDetails struct { + PropertyBag + AsOfTimeUTC *time.Time `json:"asOfTimeUtc,omitempty"` + Branch *string `json:"branch,omitempty"` + MappedTo *ArtifactLocation `json:"mappedTo,omitempty"` + RepositoryURI *string `json:"repositoryUri"` + RevisionID *string `json:"revisionId,omitempty"` + RevisionTag *string `json:"revisionTag,omitempty"` +} + +// NewVersionControlDetails creates a new VersionControlDetails and returns a pointer to it +func NewVersionControlDetails() *VersionControlDetails { + return &VersionControlDetails{} +} + +// WithAsOfTimeUTC sets the AsOfTimeUTC +func (versionControlDetails *VersionControlDetails) WithAsOfTimeUTC(asOfTimeUTC *time.Time) *VersionControlDetails { + versionControlDetails.AsOfTimeUTC = asOfTimeUTC + return versionControlDetails +} + +// WithBranch sets the Branch +func (versionControlDetails *VersionControlDetails) WithBranch(branch string) *VersionControlDetails { + versionControlDetails.Branch = &branch + return versionControlDetails +} + +// WithMappedTo sets the MappedTo +func (versionControlDetails *VersionControlDetails) WithMappedTo(mappedTo *ArtifactLocation) *VersionControlDetails { + versionControlDetails.MappedTo = mappedTo + return versionControlDetails +} + +// WithRepositoryURI sets the RepositoryURI +func (versionControlDetails *VersionControlDetails) WithRepositoryURI(repositoryURI string) *VersionControlDetails { + versionControlDetails.RepositoryURI = &repositoryURI + return versionControlDetails +} + +// WithRevisionID sets the RevisionID +func (versionControlDetails *VersionControlDetails) WithRevisionID(revisionID string) *VersionControlDetails { + versionControlDetails.RevisionID = &revisionID + return versionControlDetails +} + +// WithRevisionTag sets the RevisionTag +func (versionControlDetails *VersionControlDetails) WithRevisionTag(revisionTag string) *VersionControlDetails { + versionControlDetails.RevisionTag = &revisionTag + return versionControlDetails +} diff --git a/v2/sarif/web_request.go b/v2/sarif/web_request.go new file mode 100644 index 0000000..8a14274 --- /dev/null +++ b/v2/sarif/web_request.go @@ -0,0 +1,77 @@ +package sarif + +// WebRequest ... +type WebRequest struct { + PropertyBag + Body *ArtifactContent `json:"body,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Index *int `json:"index,omitempty"` + Method *string `json:"method,omitempty"` + Parameters map[string]string `json:"parameters,omitempty"` + Protocol *string `json:"protocol,omitempty"` + Target *string `json:"target,omitempty"` + Version *string `json:"version,omitempty"` +} + +// NewWebRequest creates a new WebRequest and returns a pointer to it +func NewWebRequest() *WebRequest { + return &WebRequest{} +} + +// WithBody sets the Body +func (webRequest *WebRequest) WithBody(body *ArtifactContent) *WebRequest { + webRequest.Body = body + return webRequest +} + +// WithHeaders sets the Headers +func (webRequest *WebRequest) WithHeaders(headers map[string]string) *WebRequest { + webRequest.Headers = headers + return webRequest +} + +// SetHeader ... +func (webRequest *WebRequest) SetHeader(name, value string) { + webRequest.Headers[name] = value +} + +// WithIndex sets the Index +func (webRequest *WebRequest) WithIndex(index int) *WebRequest { + webRequest.Index = &index + return webRequest +} + +// WithMethod sets the Method +func (webRequest *WebRequest) WithMethod(method string) *WebRequest { + webRequest.Method = &method + return webRequest +} + +// WithParameters sets the Parameters +func (webRequest *WebRequest) WithParameters(parameters map[string]string) *WebRequest { + webRequest.Parameters = parameters + return webRequest +} + +// SetParameter ... +func (webRequest *WebRequest) SetParameter(name, value string) { + webRequest.Parameters[name] = value +} + +// WithProtocol sets the Protocol +func (webRequest *WebRequest) WithProtocol(protocol string) *WebRequest { + webRequest.Protocol = &protocol + return webRequest +} + +// WithTarget sets the Target +func (webRequest *WebRequest) WithTarget(target string) *WebRequest { + webRequest.Target = &target + return webRequest +} + +// WithVersion sets the Version +func (webRequest *WebRequest) WithVersion(version string) *WebRequest { + webRequest.Version = &version + return webRequest +} diff --git a/v2/sarif/web_response.go b/v2/sarif/web_response.go new file mode 100644 index 0000000..98bbc53 --- /dev/null +++ b/v2/sarif/web_response.go @@ -0,0 +1,71 @@ +package sarif + +// WebResponse ... +type WebResponse struct { + PropertyBag + Body *ArtifactContent `json:"body,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Index *int `json:"index,omitempty"` + NoResponseReceived *bool `json:"noResponseReceived,omitempty"` + Protocol *string `json:"protocol,omitempty"` + ReasonPhrase *string `json:"reasonPhrase,omitempty"` + StatusCode *int `json:"statusCode,omitempty"` + Version *string `json:"version,omitempty"` +} + +// NewWebResponse creates a new WebResponse and returns a pointer to it +func NewWebResponse() *WebResponse { + return &WebResponse{} +} + +// WithBody sets the Body +func (webResponse *WebResponse) WithBody(body *ArtifactContent) *WebResponse { + webResponse.Body = body + return webResponse +} + +// WithHeaders sets the Headers +func (webResponse *WebResponse) WithHeaders(headers map[string]string) *WebResponse { + webResponse.Headers = headers + return webResponse +} + +// SetHeader ... +func (webResponse *WebResponse) SetHeader(name, value string) { + webResponse.Headers[name] = value +} + +// WithIndex sets the Index +func (webResponse *WebResponse) WithIndex(index int) *WebResponse { + webResponse.Index = &index + return webResponse +} + +// WithNoResponseReceived sets the NoResponseReceived +func (webResponse *WebResponse) WithNoResponseReceived(noResponse bool) *WebResponse { + webResponse.NoResponseReceived = &noResponse + return webResponse +} + +// WithProtocol sets the Protocol +func (webResponse *WebResponse) WithProtocol(protocol string) *WebResponse { + webResponse.Protocol = &protocol + return webResponse +} + +// WithReasonPhrase sets the ReasonPhrase +func (webResponse *WebResponse) WithReasonPhrase(reason string) *WebResponse { + webResponse.ReasonPhrase = &reason + return webResponse +} +// WithStatusCode sets the StatusCode +func (webResponse *WebResponse) WithStatusCode(statusCode int) *WebResponse { + webResponse.StatusCode = &statusCode + return webResponse +} + +// WithVersion sets the Version +func (webResponse *WebResponse) WithVersion(version string) *WebResponse { + webResponse.Version = &version + return webResponse +} diff --git a/v2/test/report_stage_test.go b/v2/test/report_stage_test.go new file mode 100644 index 0000000..013e4cc --- /dev/null +++ b/v2/test/report_stage_test.go @@ -0,0 +1,90 @@ +package test + +import ( + "bytes" + "testing" + + "github.com/owenrumney/go-sarif/v2/sarif" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type reportTest struct { + t *testing.T + report *sarif.Report +} + +func newReportTest(t *testing.T) (*reportTest, *reportTest, *reportTest) { + rt := &reportTest{ + t: t, + } + return rt, rt, rt +} + +func (r *reportTest) a_new_report() *reportTest { + report, err := sarif.New(sarif.Version210) + require.NoError(r.t, err) + r.report = report + return r +} + +func (r *reportTest) and() *reportTest { + return r +} + +func (r *reportTest) with_a_run_added(tool, informationUri string) *sarif.Run { + run := sarif.NewRun(tool, informationUri) + r.report.AddRun(run) + return run +} + +func (r *reportTest) with_a_run_with_empty_result_added(tool, informationUri string) *sarif.Run { + run := sarif.NewRun(tool, informationUri) + r.report.AddRun(run) + return run +} + +func (r *reportTest) an_artifact_is_added_to_the_run(run *sarif.Run, filename string) *reportTest { + a := run.AddArtifact() + a.Location = &sarif.ArtifactLocation{ + URI: &filename, + } + return r +} + +func (r *reportTest) some_properties_are_added_to_the_run(run *sarif.Run) *reportTest { + pb := sarif.NewPropertyBag() + pb.AddString("string_property", "this is a string") + pb.AddInteger("integer_property", 10) + run.AttachPropertyBag(pb) + return r +} + +func (r *reportTest) report_text_is(expected string) { + buffer := bytes.NewBufferString("") + err := r.report.Write(buffer) + require.NoError(r.t, err) + + assert.Equal(r.t, expected, buffer.String()) +} + +func (r *reportTest) a_report_is_loaded_from_a_string(content string) { + report, err := sarif.FromString(content) + assert.NoError(r.t, err) + assert.NotNil(r.t, report) + r.report = report +} + +func (r *reportTest) the_report_has_expected_driver_name_and_information_uri(driverName string, informationUri string) { + assert.Equal(r.t, driverName, r.report.Runs[0].Tool.Driver.Name) + assert.Equal(r.t, informationUri, *r.report.Runs[0].Tool.Driver.InformationURI) +} + +func (r *reportTest) a_report_is_loaded_from_a_file(filename string) { + report, err := sarif.Open(filename) + if err != nil { + panic(err) + } + r.report = report + +} diff --git a/v2/test/report_test.go b/v2/test/report_test.go new file mode 100644 index 0000000..5bd4ccb --- /dev/null +++ b/v2/test/report_test.go @@ -0,0 +1,124 @@ +package test + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_new_simple_report_with_single_run(t *testing.T) { + given, _, then := newReportTest(t) + + given.a_new_report(). + with_a_run_added("tfsec", "https://tfsec.dev") + then.report_text_is(`{"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0-rtm.5.json","runs":[{"tool":{"driver":{"name":"tfsec","informationUri":"https://tfsec.dev"}},"results":[]}]}`) +} + +func Test_new_report_with_empty_run(t *testing.T) { + given, _, then := newReportTest(t) + + given.a_new_report(). + with_a_run_with_empty_result_added("tfsec", "https://tfsec.dev") + then.report_text_is(`{"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0-rtm.5.json","runs":[{"tool":{"driver":{"name":"tfsec","informationUri":"https://tfsec.dev"}},"results":[]}]}`) +} + +func Test_new_simple_report_with_artifact(t *testing.T) { + given, when, then := newReportTest(t) + + run := given.a_new_report(). + with_a_run_added("tfsec", "https://tfsec.dev") + when.an_artifact_is_added_to_the_run(run, "file://broken.go") + then.report_text_is(`{"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0-rtm.5.json","runs":[{"tool":{"driver":{"name":"tfsec","informationUri":"https://tfsec.dev"}},"artifacts":[{"location":{"uri":"file://broken.go"},"length":-1}],"results":[]}]}`) +} + +func Test_new_simple_report_with_propertybag(t *testing.T) { + given, when, then := newReportTest(t) + + run := given.a_new_report(). + with_a_run_added("tfsec", "https://tfsec.dev") + when.some_properties_are_added_to_the_run(run) + then.report_text_is(`{"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0-rtm.5.json","runs":[{"tool":{"driver":{"name":"tfsec","informationUri":"https://tfsec.dev"}},"results":[],"properties":{"integer_property":10,"string_property":"this is a string"}}]}`) +} + +func Test_new_simple_report_with_duplicate_artifact(t *testing.T) { + given, when, then := newReportTest(t) + + run := given.a_new_report(). + with_a_run_added("tfsec", "https://tfsec.dev") + when.an_artifact_is_added_to_the_run(run, "file://broken.go"). + and(). + an_artifact_is_added_to_the_run(run, "file://broken.go") + then.report_text_is(`{"version":"2.1.0","$schema":"https://json.schemastore.org/sarif-2.1.0-rtm.5.json","runs":[{"tool":{"driver":{"name":"tfsec","informationUri":"https://tfsec.dev"}},"artifacts":[{"location":{"uri":"file://broken.go"},"length":-1},{"location":{"uri":"file://broken.go"},"length":-1}],"results":[]}]}`) +} + +func Test_load_sarif_from_string(t *testing.T) { + given, _, then := newReportTest(t) + + content := `{ + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json", + "runs": [ + { + "tool": { + "driver": { + "name": "ESLint", + "informationUri": "https://eslint.org" + } + } + } + ] +}` + + given.a_report_is_loaded_from_a_string(content) + then.the_report_has_expected_driver_name_and_information_uri("ESLint", "https://eslint.org") +} + +func Test_load_sarif_report_from_file(t *testing.T) { + given, _, then := newReportTest(t) + + content := `{ + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json", + "runs": [ + { + "tool": { + "driver": { + "name": "ESLint", + "informationUri": "https://eslint.org" + } + } + } + ] +}` + + file, err := ioutil.TempFile(os.TempDir(), "sarifReport") + assert.NoError(t, err) + defer file.Close() + + ioutil.WriteFile(file.Name(), []byte(content), 755) + + given.a_report_is_loaded_from_a_file(file.Name()) + then.the_report_has_expected_driver_name_and_information_uri("ESLint", "https://eslint.org") +} + +func Test_err_on_load_sarif_report_from_file_when_not_exists(t *testing.T) { + given, _, _ := newReportTest(t) + defer func() { + if err := recover().(error); err != nil { + assert.Equal(t, "the provided file path doesn't have a file", err.Error()) + } + }() + given.a_report_is_loaded_from_a_file("") +} + +func Test_err_on_load_sarif_report_from_file_when_file_not_legit(t *testing.T) { + given, _, _ := newReportTest(t) + defer func() { + if err := recover().(error); err != nil { + assert.Equal(t, "the provided filepath could not be opened. read /tmp: is a directory", err.Error()) + } + }() + given.a_report_is_loaded_from_a_file("/tmp") +} diff --git a/v2/test/run_stage_test.go b/v2/test/run_stage_test.go new file mode 100644 index 0000000..7e5e31a --- /dev/null +++ b/v2/test/run_stage_test.go @@ -0,0 +1,50 @@ +package test + +import ( + "encoding/json" + "testing" + + "github.com/owenrumney/go-sarif/v2/sarif" + "github.com/stretchr/testify/assert" +) + +type runTest struct { + t *testing.T + run *sarif.Run + jsonString string +} + +func newRunTest(t *testing.T) (*runTest, *runTest, *runTest) { + r := &runTest{ + t: t, + run: &sarif.Run{ + Tool: sarif.Tool{}, + Artifacts: []*sarif.Artifact{}, + Results: []*sarif.Result{}, + }, + } + return r, r, r +} + +func (r *runTest) a_result_is_added() { + r.run.AddResult("rule1") +} + +func (r *runTest) properties_added_to_a_run() { + pb := sarif.NewPropertyBag() + + pb.AddString("string_key", "string_value") + pb.AddBoolean("boolean_key", false) + + r.run.AttachPropertyBag(pb) +} + +func (r *runTest) the_run_is_json() { + b, err := json.Marshal(r.run) + assert.Nil(r.t, err) + r.jsonString = string(b) +} + +func (r *runTest) string_is_as_expected(expected string) { + assert.Equal(r.t, expected, r.jsonString) +} diff --git a/v2/test/run_test.go b/v2/test/run_test.go new file mode 100644 index 0000000..7d80191 --- /dev/null +++ b/v2/test/run_test.go @@ -0,0 +1,19 @@ +package test + +import "testing" + +func Test_new_result_on_run(t *testing.T) { + given, _, _ := newRunTest(t) + + given.a_result_is_added() +} + +func Test_properties_on_a_run(t *testing.T) { + given, when, then := newRunTest(t) + + expected := `{"tool":{"driver":null},"results":[],"properties":{"boolean_key":false,"string_key":"string_value"}}` + + given.properties_added_to_a_run() + when.the_run_is_json() + then.string_is_as_expected(expected) +}