Repository: tenable/terrascan (language: python)

Issue:
Sarif output has wrong file path value for file scans
### Directory Scan
```
./bin/terrascan scan -d pkg/iac-providers/terraform/v14/testdata/relative-moduleconfigs/elasticcache/ -o sarif                                                                                                                                        
{
  "version": "2.1.0",
  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "terrascan",
          "version": "1.7.0",
          "informationUri": "https://github.com/accurics/terrascan",
          "rules": [
            {
              "id": "AWS.ElastiCache.DataSecurity.High.0424",
              "name": "noMemcachedInElastiCache",
              "shortDescription": {
                "text": "ElastiCache for Memcached is not in use in AWS PCI DSS environments"
              },
              "properties": {
                "category": "Compliance Validation",
                "severity": "HIGH"
              }
            }
          ]
        }
      },
      "results": [
        {
          "ruleId": "AWS.ElastiCache.DataSecurity.High.0424",
          "level": "error",
          "message": {
            "text": "ElastiCache for Memcached is not in use in AWS PCI DSS environments"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "file:///Users/dev-gaur/go/src/github.com/accurics/terrascan/pkg/iac-providers/terraform/v14/testdata/relative-moduleconfigs/elasticcache/main.tf"
                },
                "region": {
                  "startLine": 1
                }
              },
              "logicalLocations": [
                {
                  "name": "noMemcachedInElastiCache",
                  "kind": "aws_elasticache_cluster"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

Check the artifactLocation.url value. It works fine for Directory scans, but works a bit wrong for File scans. Check the example below.

### File Scan
```
./bin/terrascan scan -f pkg/iac-providers/terraform/v14/testdata/relative-moduleconfigs/elasticcache/main.tf -o sarif                                                                                                                                 
{
  "version": "2.1.0",
  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "terrascan",
          "version": "1.7.0",
          "informationUri": "https://github.com/accurics/terrascan",
          "rules": [
            {
              "id": "AWS.ElastiCache.DataSecurity.High.0424",
              "name": "noMemcachedInElastiCache",
              "shortDescription": {
                "text": "ElastiCache for Memcached is not in use in AWS PCI DSS environments"
              },
              "properties": {
                "category": "Compliance Validation",
                "severity": "HIGH"
              }
            }
          ]
        }
      },
      "results": [
        {
          "ruleId": "AWS.ElastiCache.DataSecurity.High.0424",
          "level": "error",
          "message": {
            "text": "ElastiCache for Memcached is not in use in AWS PCI DSS environments"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "file:///Users/dev-gaur/go/src/github.com/accurics/terrascan/pkg/iac-providers/terraform/v14/testdata/relative-moduleconfigs/elasticcache/main.tf/main.tf"
                },
                "region": {
                  "startLine": 1
                }
              },
              "logicalLocations": [
                {
                  "name": "noMemcachedInElastiCache",
                  "kind": "aws_elasticache_cluster"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```


Discussion / hints:

Fix patch:
diff --git a/go.mod b/go.mod
index bbddf9d18..eaa1eed38 100644
--- a/go.mod
+++ b/go.mod
@@ -38,8 +38,8 @@ require (
 	github.com/spf13/cobra v1.1.1
 	github.com/zclconf/go-cty v1.8.2
 	go.uber.org/zap v1.16.0
-	golang.org/x/sys v0.0.0-20210611083646-a4fc73990273
-	golang.org/x/tools v0.1.3 // indirect
+	golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
+	golang.org/x/tools v0.1.4 // indirect
 	gopkg.in/src-d/go-git.v4 v4.13.1
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 	helm.sh/helm/v3 v3.4.0
diff --git a/go.sum b/go.sum
index 6bc42f736..dcc81ceab 100644
--- a/go.sum
+++ b/go.sum
@@ -1177,10 +1177,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk=
 golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8=
-golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ=
-golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1256,10 +1254,8 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u
 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs=
+golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1365,7 +1361,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
@@ -1383,7 +1378,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
 gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
-gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
 gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
 gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
diff --git a/pkg/utils/dir.go b/pkg/utils/dir.go
index b3111385f..4c609a3f3 100644
--- a/pkg/utils/dir.go
+++ b/pkg/utils/dir.go
@@ -1,3 +1,19 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
 package utils
 
 import (
@@ -30,7 +46,7 @@ func GenerateTempDir() string {
 func IsDirExists(dir string) bool {
 	_, err := os.Stat(dir)
 	if os.IsNotExist(err) {
-		zap.S().Debug("Directory %s does not exist.", dir)
+		zap.S().Errorf("directory %s does not exist.", dir)
 		return false
 	}
 	return true
diff --git a/pkg/utils/file.go b/pkg/utils/file.go
new file mode 100644
index 000000000..e05e4b4af
--- /dev/null
+++ b/pkg/utils/file.go
@@ -0,0 +1,37 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+package utils
+
+import (
+	"go.uber.org/zap"
+	"os"
+)
+
+// GetFileMode fetches the filemode from a file path
+func GetFileMode(path string) *os.FileMode {
+	fi, err := os.Stat(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			zap.S().Errorf("file %s does not exist.", path)
+		} else {
+			zap.S().Errorf("unable to fetch file info for path %s.", path)
+		}
+		return nil
+	}
+	mode := fi.Mode()
+	return &mode
+}
diff --git a/pkg/utils/json.go b/pkg/utils/json.go
index 138f6da99..95d125f0b 100644
--- a/pkg/utils/json.go
+++ b/pkg/utils/json.go
@@ -1,3 +1,19 @@
+/*
+    Copyright (C) 2020 Accurics, Inc.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
 package utils
 
 import (
@@ -56,19 +72,24 @@ func LoadJSON(filePath string) ([]*IacDocument, error) {
 
 // AreEqualJSON validate if two json strings are equal
 func AreEqualJSON(s1, s2 string) (bool, error) {
+	return AreEqualJSONBytes([]byte(s1), []byte(s2))
+}
+
+// AreEqualJSONBytes validate if two json byte arrays are equal
+func AreEqualJSONBytes(b1, b2 []byte) (bool, error) {
 	var o1 interface{}
 	var o2 interface{}
 
-	errmsg := "error json unmarshalling string: %s. error: %v"
+	errmsg := "error json unmarshalling bytes: %s. error: %v"
 
 	var err error
-	err = json.Unmarshal([]byte(s1), &o1)
+	err = json.Unmarshal(b1, &o1)
 	if err != nil {
-		return false, fmt.Errorf(errmsg, s1, err.Error())
+		return false, fmt.Errorf(errmsg, b1, err.Error())
 	}
-	err = json.Unmarshal([]byte(s2), &o2)
+	err = json.Unmarshal(b2, &o2)
 	if err != nil {
-		return false, fmt.Errorf(errmsg, s2, err.Error())
+		return false, fmt.Errorf(errmsg, b2, err.Error())
 	}
 
 	return reflect.DeepEqual(o1, o2), nil
diff --git a/pkg/writer/sarif.go b/pkg/writer/sarif.go
index 4bea5b460..d25bad505 100644
--- a/pkg/writer/sarif.go
+++ b/pkg/writer/sarif.go
@@ -19,8 +19,10 @@ package writer
 import (
 	"fmt"
 	"github.com/accurics/terrascan/pkg/policy"
+	"github.com/accurics/terrascan/pkg/utils"
 	"github.com/accurics/terrascan/pkg/version"
 	"github.com/owenrumney/go-sarif/sarif"
+	"go.uber.org/zap"
 	"io"
 	"path/filepath"
 	"strings"
@@ -56,7 +58,7 @@ func SarifWriter(data interface{}, writer io.Writer) error {
 		run.AddRule(string(passedRule.RuleID)).
 			WithDescription(passedRule.Description).WithName(passedRule.RuleName).WithProperties(m)
 	}
-	resourcePath := outputData.Summary.ResourcePath
+
 	// for each result add the rule, location and result to the report
 	for _, violation := range outputData.Violations {
 		m := make(map[string]string)
@@ -66,9 +68,10 @@ func SarifWriter(data interface{}, writer io.Writer) error {
 		rule := run.AddRule(string(violation.RuleID)).
 			WithDescription(violation.Description).WithName(violation.RuleName).WithProperties(m)
 
-		absFilePath := violation.File
-		if !filepath.IsAbs(violation.File) {
-			absFilePath = filepath.Join(resourcePath, violation.File)
+		absFilePath, err := getAbsoluteFilePath(outputData.Summary.ResourcePath, violation.File)
+
+		if err != nil {
+			return err
 		}
 
 		location := sarif.NewLocation().
@@ -99,3 +102,18 @@ func getSarifLevel(severity string) string {
 
 	return m[strings.ToLower(severity)]
 }
+
+func getAbsoluteFilePath(resourcePath, filePath string) (string, error) {
+	if !filepath.IsAbs(resourcePath) {
+		resourcePath, err := filepath.Abs(resourcePath)
+		if err != nil {
+			zap.S().Errorf("unable to get absolute path for %s, error: %v", resourcePath, err)
+			return "", err
+		}
+	}
+	fileMode := utils.GetFileMode(resourcePath)
+	if fileMode != nil && (*fileMode).IsDir() {
+		return filepath.Join(resourcePath, filePath), nil
+	}
+	return resourcePath, nil
+}


Test patch:
diff --git a/pkg/writer/sarif_test.go b/pkg/writer/sarif_test.go
index 4996b243a..93a99970d 100644
--- a/pkg/writer/sarif_test.go
+++ b/pkg/writer/sarif_test.go
@@ -12,6 +12,8 @@ import (
 	"github.com/accurics/terrascan/pkg/version"
 )
 
+var testpath, _ = getAbsoluteFilePath(violationsInput.Summary.ResourcePath, violationsInput.Violations[0].File)
+
 var expectedSarifOutput1 = fmt.Sprintf(`{
           "version": "2.1.0",
           "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
@@ -48,7 +50,7 @@ var expectedSarifOutput1 = fmt.Sprintf(`{
                     {
                       "physicalLocation": {
                         "artifactLocation": {
-                          "uri": "file://test/modules/m1/main.tf"
+                          "uri": "%s"
                         },
                         "region": {
                           "startLine": 20
@@ -66,7 +68,7 @@ var expectedSarifOutput1 = fmt.Sprintf(`{
               ]
             }
           ]
-        }`, version.GetNumeric())
+        }`, version.GetNumeric(), fmt.Sprintf("file://%s", testpath))
 
 var expectedSarifOutput2 = fmt.Sprintf(`{
           "version": "2.1.0",
diff --git a/pkg/writer/test/dummy b/pkg/writer/test/dummy
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/e2e/scan/scan_k8s_files_test.go b/test/e2e/scan/scan_k8s_files_test.go
index b6c49a9e9..f73d1615f 100644
--- a/test/e2e/scan/scan_k8s_files_test.go
+++ b/test/e2e/scan/scan_k8s_files_test.go
@@ -17,6 +17,7 @@
 package scan_test
 
 import (
+	"github.com/accurics/terrascan/pkg/version"
 	"path/filepath"
 
 	scanUtils "github.com/accurics/terrascan/test/e2e/scan"
@@ -26,7 +27,7 @@ import (
 	"github.com/onsi/gomega/gbytes"
 )
 
-var _ = Describe("Scan is run for k8s files", func() {
+var _ = Describe("Scan is run for k8s directories and files", func() {
 
 	BeforeEach(func() {
 		outWriter = gbytes.NewBuffer()
@@ -38,16 +39,16 @@ var _ = Describe("Scan is run for k8s files", func() {
 		errWriter = nil
 	})
 
-	Context("scan iac files violating k8s policies", func() {
-		var policyDir, iacDir string
-		policyDir, err1 := filepath.Abs(policyRootRelPath)
-		iacDir, err2 := filepath.Abs(filepath.Join(k8sIacRelPath, "kubernetes_ingress_violation"))
+	var policyDir, iacDir string
+	policyDir, err1 := filepath.Abs(policyRootRelPath)
+	iacDir, err2 := filepath.Abs(filepath.Join(k8sIacRelPath, "kubernetes_ingress_violation"))
 
-		It("should not error out while getting absolute path", func() {
-			Expect(err1).NotTo(HaveOccurred())
-			Expect(err2).NotTo(HaveOccurred())
-		})
+	It("should not error out while getting absolute path", func() {
+		Expect(err1).NotTo(HaveOccurred())
+		Expect(err2).NotTo(HaveOccurred())
+	})
 
+	Context("scan iac directories violating k8s policies", func() {
 		Context("iac type k8s will be part of all iac", func() {
 			When("k8s files are scanned but iac type is not specified", func() {
 				It("should scan will all iac and display violations", func() {
@@ -74,6 +75,15 @@ var _ = Describe("Scan is run for k8s files", func() {
 				})
 			})
 
+			When("when output type is sarif", func() {
+				It("should display violations in sarif format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-d", iacDir, "-o", "sarif"}
+					path, _ := helper.GetAbsoluteFilePathForSarif(iacDir, "config.yaml")
+					golden := scanUtils.GetSarifGoldenString(scanUtils.SarifTemplateK8sTLSViolation, version.GetNumeric(), path)
+					scanUtils.RunScanAndAssertJSONOutputString(terrascanBinaryPath, golden, helper.ExitCodeThree, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
 			When("when output type is json", func() {
 				It("should display violations in json format", func() {
 					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-d", iacDir, "-o", "json"}
@@ -103,4 +113,76 @@ var _ = Describe("Scan is run for k8s files", func() {
 			})
 		})
 	})
+
+	Context("scan iac files violating k8s policies", func() {
+		iacFile := filepath.Join(iacDir, "config.yaml")
+		It("should not error out while getting absolute path", func() {
+			Expect(err1).NotTo(HaveOccurred())
+			Expect(err2).NotTo(HaveOccurred())
+		})
+
+		Context("iac type k8s will be part of all iac", func() {
+			When("k8s files are scanned but iac type is not specified", func() {
+				It("should scan will all iac and display violations", func() {
+					scanArgs := []string{scanUtils.ScanCommand, "-f", iacFile}
+					session = helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, scanArgs...)
+					// exit code is 1 because iac file is expected to be of terraform iac type by default, not k8s yaml
+					helper.ValidateExitCode(session, scanUtils.ScanTimeout, helper.ExitCodeOne)
+				})
+			})
+		})
+
+		k8sGoldenRelPath := filepath.Join("golden", "k8s_scans", "k8s", "kubernetes_ingress_violations")
+
+		Context("iac type is specified as k8s", func() {
+			It("should scan and display violations in human output format", func() {
+				scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile}
+				scanUtils.RunScanAndAssertGoldenOutputRegex(terrascanBinaryPath, filepath.Join(k8sGoldenRelPath, "kubernetes_ingress_human.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...)
+			})
+
+			When("-v flag is used for verbose output", func() {
+				It("should display verbose output for human output format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile, "-v"}
+					scanUtils.RunScanAndAssertGoldenOutputRegex(terrascanBinaryPath, filepath.Join(k8sGoldenRelPath, "kubernetes_ingress_human_verbose.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
+			When("when output type is sarif", func() {
+				It("should display violations in sarif format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile, "-o", "sarif"}
+					path, _ := helper.GetAbsoluteFilePathForSarif(iacFile, "config.yaml")
+					golden := scanUtils.GetSarifGoldenString(scanUtils.SarifTemplateK8sTLSViolation, version.GetNumeric(), path)
+					scanUtils.RunScanAndAssertJSONOutputString(terrascanBinaryPath, golden, helper.ExitCodeThree, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
+			When("when output type is json", func() {
+				It("should display violations in json format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile, "-o", "json"}
+					scanUtils.RunScanAndAssertGoldenOutputRegex(terrascanBinaryPath, filepath.Join(k8sGoldenRelPath, "kubernetes_ingress_json.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
+			When("when output type is yaml", func() {
+				It("should display violations in yaml format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile, "-o", "yaml"}
+					scanUtils.RunScanAndAssertGoldenOutputRegex(terrascanBinaryPath, filepath.Join(k8sGoldenRelPath, "kubernetes_ingress_yaml.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
+			When("when output type is xml", func() {
+				It("should display violations in xml format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile, "-o", "xml"}
+					scanUtils.RunScanAndAssertGoldenOutputRegex(terrascanBinaryPath, filepath.Join(k8sGoldenRelPath, "kubernetes_ingress_xml.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
+			When("when output type is junit-xml", func() {
+				It("should display violations in junit-xml format", func() {
+					scanArgs := []string{"-i", "k8s", "-p", policyDir, "-f", iacFile, "-o", "junit-xml"}
+					scanUtils.RunScanAndAssertGoldenOutputRegex(terrascanBinaryPath, filepath.Join(k8sGoldenRelPath, "kubernetes_ingress_junit_xml.txt"), helper.ExitCodeThree, true, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+		})
+	})
 })
diff --git a/test/e2e/scan/scan_tf_files_test.go b/test/e2e/scan/scan_tf_files_test.go
index d2308b9a7..1eba8579e 100644
--- a/test/e2e/scan/scan_tf_files_test.go
+++ b/test/e2e/scan/scan_tf_files_test.go
@@ -17,6 +17,7 @@
 package scan_test
 
 import (
+	"github.com/accurics/terrascan/pkg/version"
 	"path/filepath"
 
 	scanUtils "github.com/accurics/terrascan/test/e2e/scan"
@@ -117,6 +118,15 @@ var _ = Describe("Scan is run for terraform files", func() {
 				})
 			})
 
+			When("output type is sarif", func() {
+				It("should display violations in sarif format", func() {
+					scanArgs := []string{"-p", policyDir, "-i", "terraform", "-d", iacDir, "-o", "sarif"}
+					path, _ := helper.GetAbsoluteFilePathForSarif(iacDir, "main.tf")
+					golden := scanUtils.GetSarifGoldenString(scanUtils.SarifTemplateAWSAMIViolation, version.GetNumeric(), path)
+					scanUtils.RunScanAndAssertJSONOutputString(terrascanBinaryPath, golden, helper.ExitCodeThree, true, outWriter, errWriter, scanArgs...)
+				})
+			})
+
 			When("output type is json and no iac type is specified", func() {
 				Context("when iac type is not specified and a directory is specified, it will be scanned will all iac providers", func() {
 					It("should display violations in json format, and should have iac type as 'all'", func() {
diff --git a/test/e2e/scan/scan_utils.go b/test/e2e/scan/scan_utils.go
index 1fa41f711..e4f549a37 100644
--- a/test/e2e/scan/scan_utils.go
+++ b/test/e2e/scan/scan_utils.go
@@ -17,6 +17,7 @@
 package scan
 
 import (
+	"fmt"
 	"io"
 	"path/filepath"
 
@@ -55,6 +56,15 @@ func RunScanAndAssertJSONOutput(terrascanBinaryPath, relGoldenFilePath string, e
 	helper.CompareActualWithGoldenJSON(session, goldenFileAbsPath, isStdOut)
 }
 
+// RunScanAndAssertJSONOutputString runs the scan command with supplied paramters and compares actual and golden output
+func RunScanAndAssertJSONOutputString(terrascanBinaryPath, goldenString string, exitCode int, isStdOut bool, outWriter, errWriter io.Writer, args ...string) {
+	argList := []string{ScanCommand}
+	argList = append(argList, args...)
+	session := helper.RunCommand(terrascanBinaryPath, outWriter, errWriter, argList...)
+	gomega.Eventually(session, ScanTimeout).Should(gexec.Exit(exitCode))
+	helper.CompareActualWithGoldenJSONString(session, goldenString, isStdOut)
+}
+
 // RunScanAndAssertYAMLOutput runs the scan command with supplied paramters and compares actual and golden output
 func RunScanAndAssertYAMLOutput(terrascanBinaryPath, relGoldenFilePath string, exitCode int, isJunitXML, isStdOut bool, outWriter, errWriter io.Writer, args ...string) {
 	session, goldenFileAbsPath := RunScanCommand(terrascanBinaryPath, relGoldenFilePath, exitCode, outWriter, errWriter, args...)
@@ -84,3 +94,123 @@ func RunScanCommand(terrascanBinaryPath, relGoldenFilePath string, exitCode int,
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 	return session, goldenFileAbsPath
 }
+
+// GetSarifGoldenString gives out golden sarif format string
+func GetSarifGoldenString(template, terrascanVersion string, absfilepath string) string {
+	return fmt.Sprintf(template, terrascanVersion, fmt.Sprintf("file://%s", absfilepath))
+}
+
+// SarifTemplateAWSAMIViolation string
+const SarifTemplateAWSAMIViolation = `{
+  "version": "2.1.0",
+  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+  "runs": [
+    {
+      "tool": {
+        "driver": {
+          "name": "terrascan",
+          "version": "%s",
+          "informationUri": "https://github.com/accurics/terrascan",
+          "rules": [
+            {
+              "id": "AC_AWS_0001",
+              "name": "amiNotEncrypted",
+              "shortDescription": {
+                "text": "Enable AWS AMI Encryption"
+              },
+              "properties": {
+                "category": "Encryption \u0026 KeyManagement",
+                "severity": "MEDIUM"
+              }
+            }
+          ]
+        }
+      },
+      "results": [
+        {
+          "ruleId": "AC_AWS_0001",
+          "level": "warning",
+          "message": {
+            "text": "Enable AWS AMI Encryption"
+          },
+          "locations": [
+            {
+              "physicalLocation": {
+                "artifactLocation": {
+                  "uri": "%s"
+                },
+                "region": {
+                  "startLine": 5
+                }
+              },
+              "logicalLocations": [
+                {
+                  "name": "awsAmiEncrypted",
+                  "kind": "aws_ami"
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}`
+
+// SarifTemplateK8sTLSViolation string template
+const SarifTemplateK8sTLSViolation = `{
+  "version": "2.1.0",
+  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+  "runs": [
+    {
+      "tool": {
+        "driver": {
+          "name": "terrascan",
+          "version": "%s",
+          "informationUri": "https://github.com/accurics/terrascan",
+          "rules": [
+            {
+              "id": "AC_K8S_0001",
+              "name": "noHttps",
+              "shortDescription": {
+                "text": "TLS disabled can affect the confidentiality of the data in transit"
+              },
+              "properties": {
+                "category": "Network Security",
+                "severity": "HIGH"
+              }
+            }
+          ]
+        }
+      },
+      "results": [
+        {
+          "ruleId": "AC_K8S_0001",
+          "level": "error",
+          "message": {
+            "text": "TLS disabled can affect the confidentiality of the data in transit"
+          },
+          "locations": [
+            {
+              "physicalLocation": {
+                "artifactLocation": {
+                  "uri": "%s"
+                },
+                "region": {
+                  "startLine": 1
+                }
+              },
+              "logicalLocations": [
+                {
+                  "name": "ingress-demo-disallowed",
+                  "kind": "kubernetes_ingress"
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+`
diff --git a/test/helper/helper.go b/test/helper/helper.go
index f486ad768..f57c8d3e0 100644
--- a/test/helper/helper.go
+++ b/test/helper/helper.go
@@ -20,10 +20,12 @@ import (
 	"bytes"
 	"encoding/json"
 	"encoding/xml"
+	"go.uber.org/zap"
 	"io"
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"reflect"
 	"regexp"
 	"sort"
@@ -186,6 +188,24 @@ func CompareActualWithGoldenJSON(session *gexec.Session, goldenFileAbsPath strin
 	CompareSummaryAndViolations(sessionEngineOutput, fileDataEngineOutput)
 }
 
+// CompareActualWithGoldenJSONString compares actual data with golden json string passed as parameter
+func CompareActualWithGoldenJSONString(session *gexec.Session, golden string, isStdOut bool) {
+	goldenBytes := []byte(golden)
+
+	var sessionBytes []byte
+
+	if isStdOut {
+		sessionBytes = session.Wait().Out.Contents()
+	} else {
+		sessionBytes = session.Wait().Err.Contents()
+	}
+
+	sessionBytes = bytes.TrimSpace(sessionBytes)
+	goldenBytes = bytes.TrimSpace(goldenBytes)
+
+	gomega.Expect(utils.AreEqualJSONBytes(sessionBytes, goldenBytes)).To(gomega.BeTrue())
+}
+
 // CompareActualWithGoldenYAML compares actual data with contents of golden file passed as parameter
 func CompareActualWithGoldenYAML(session *gexec.Session, goldenFileAbsPath string, isStdOut bool) {
 	sessionBytes, fileBytes := GetByteData(session, goldenFileAbsPath, isStdOut)
@@ -352,3 +372,19 @@ func removeFileAndRoothFromViolations(v violations) {
 		violation.PlanRoot = ""
 	}
 }
+
+// GetAbsoluteFilePathForSarif helper for sarif path
+func GetAbsoluteFilePathForSarif(resourcePath, filePath string) (string, error) {
+	if !filepath.IsAbs(resourcePath) {
+		resourcePath, err := filepath.Abs(resourcePath)
+		if err != nil {
+			zap.S().Errorf("unable to get absolute path for %s, error: %v", resourcePath, err)
+			return "", err
+		}
+	}
+	mode := utils.GetFileMode(resourcePath)
+	if mode != nil && (*mode).IsDir() {
+		return filepath.Join(resourcePath, filePath), nil
+	}
+	return resourcePath, nil
+}