github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/test/ignore_test.go (about)

     1  package test
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/aquasecurity/defsec/pkg/providers"
     9  	"github.com/aquasecurity/defsec/pkg/rules"
    10  	"github.com/aquasecurity/defsec/pkg/scan"
    11  	"github.com/aquasecurity/defsec/pkg/severity"
    12  	"github.com/aquasecurity/defsec/pkg/terraform"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  var exampleRule = scan.Rule{
    17  	Provider:  providers.AWSProvider,
    18  	Service:   "service",
    19  	ShortCode: "abc123",
    20  	AVDID:     "AWS-ABC-123",
    21  	Aliases:   []string{"aws-other-abc123"},
    22  	Severity:  severity.High,
    23  	CustomChecks: scan.CustomChecks{
    24  		Terraform: &scan.TerraformCustomCheck{
    25  			RequiredLabels: []string{"bad"},
    26  			Check: func(resourceBlock *terraform.Block, _ *terraform.Module) (results scan.Results) {
    27  				attr := resourceBlock.GetAttribute("secure")
    28  				if attr.IsNil() {
    29  					results.Add("example problem", resourceBlock)
    30  				}
    31  				if attr.IsFalse() {
    32  					results.Add("example problem", attr)
    33  				}
    34  				return
    35  			},
    36  		},
    37  	},
    38  }
    39  
    40  func Test_IgnoreAll(t *testing.T) {
    41  
    42  	var testCases = []struct {
    43  		name         string
    44  		inputOptions string
    45  		assertLength int
    46  	}{
    47  		{name: "IgnoreAll", inputOptions: `
    48  resource "bad" "my-rule" {
    49      secure = false // tfsec:ignore:*
    50  }
    51  `, assertLength: 0},
    52  		{name: "IgnoreLineAboveTheBlock", inputOptions: `
    53  // tfsec:ignore:*
    54  resource "bad" "my-rule" {
    55     secure = false 
    56  }
    57  `, assertLength: 0},
    58  		{name: "IgnoreLineAboveTheBlockMatchingParamBool", inputOptions: `
    59  // tfsec:ignore:*[secure=false]
    60  resource "bad" "my-rule" {
    61     secure = false
    62  }
    63  `, assertLength: 0},
    64  		{name: "IgnoreLineAboveTheBlockNotMatchingParamBool", inputOptions: `
    65  // tfsec:ignore:*[secure=true]
    66  resource "bad" "my-rule" {
    67     secure = false 
    68  }
    69  `, assertLength: 1},
    70  		{name: "IgnoreLineAboveTheBlockMatchingParamString", inputOptions: `
    71  // tfsec:ignore:*[name=myrule]
    72  resource "bad" "my-rule" {
    73      name = "myrule"
    74      secure = false
    75  }
    76  `, assertLength: 0},
    77  		{name: "IgnoreLineAboveTheBlockNotMatchingParamString", inputOptions: `
    78  // tfsec:ignore:*[name=myrule2]
    79  resource "bad" "my-rule" {
    80      name = "myrule"
    81      secure = false 
    82  }
    83  `, assertLength: 1},
    84  		{name: "IgnoreLineAboveTheBlockMatchingParamInt", inputOptions: `
    85  // tfsec:ignore:*[port=123]
    86  resource "bad" "my-rule" {
    87     secure = false
    88     port = 123
    89  }
    90  `, assertLength: 0},
    91  		{name: "IgnoreLineAboveTheBlockNotMatchingParamInt", inputOptions: `
    92  // tfsec:ignore:*[port=456]
    93  resource "bad" "my-rule" {
    94     secure = false 
    95     port = 123
    96  }
    97  `, assertLength: 1},
    98  		{name: "IgnoreLineStackedAboveTheBlock", inputOptions: `
    99  // tfsec:ignore:*
   100  // tfsec:ignore:a
   101  // tfsec:ignore:b
   102  // tfsec:ignore:c
   103  // tfsec:ignore:d
   104  resource "bad" "my-rule" {
   105     secure = false 
   106  }
   107  `, assertLength: 0},
   108  		{name: "IgnoreLineStackedAboveTheBlockWithoutMatch", inputOptions: `
   109  #tfsec:ignore:*
   110  
   111  #tfsec:ignore:x
   112  #tfsec:ignore:a
   113  #tfsec:ignore:b
   114  #tfsec:ignore:c
   115  #tfsec:ignore:d
   116  resource "bad" "my-rule" {
   117     secure = false 
   118  }
   119  `, assertLength: 1},
   120  		{name: "IgnoreLineStackedAboveTheBlockWithHashesWithoutSpaces", inputOptions: `
   121  #tfsec:ignore:*
   122  #tfsec:ignore:a
   123  #tfsec:ignore:b
   124  #tfsec:ignore:c
   125  #tfsec:ignore:d
   126  resource "bad" "my-rule" {
   127     secure = false 
   128  }
   129  `, assertLength: 0},
   130  		{name: "IgnoreLineStackedAboveTheBlockWithoutSpaces", inputOptions: `
   131  //tfsec:ignore:*
   132  //tfsec:ignore:a
   133  //tfsec:ignore:b
   134  //tfsec:ignore:c
   135  //tfsec:ignore:d
   136  resource "bad" "my-rule" {
   137     secure = false 
   138  }
   139  `, assertLength: 0},
   140  		{name: "IgnoreLineAboveTheLine", inputOptions: `
   141  resource "bad" "my-rule" {
   142  	# tfsec:ignore:aws-service-abc123
   143      secure = false
   144  }
   145  `, assertLength: 0},
   146  		{name: "IgnoreWithExpDateIfDateBreachedThenDontIgnore", inputOptions: `
   147  resource "bad" "my-rule" {
   148      secure = false # tfsec:ignore:aws-service-abc123:exp:2000-01-02
   149  }
   150  `, assertLength: 1},
   151  		{name: "IgnoreWithExpDateIfDateNotBreachedThenIgnoreIgnore", inputOptions: `
   152  resource "bad" "my-rule" {
   153      secure = false # tfsec:ignore:aws-service-abc123:exp:2221-01-02
   154  }
   155  `, assertLength: 0},
   156  		{name: "IgnoreWithExpDateIfDateInvalidThenDropTheIgnore", inputOptions: `
   157  resource "bad" "my-rule" {
   158     secure = false # tfsec:ignore:aws-service-abc123:exp:2221-13-02
   159  }
   160  `, assertLength: 1},
   161  		{name: "IgnoreAboveResourceBlockWithExpDateIfDateNotBreachedThenIgnoreIgnore", inputOptions: `
   162  #tfsec:ignore:aws-service-abc123:exp:2221-01-02
   163  resource "bad" "my-rule" {
   164  }
   165  `, assertLength: 0},
   166  		{name: "IgnoreAboveResourceBlockWithExpDateAndMultipleIgnoresIfDateNotBreachedThenIgnoreIgnore", inputOptions: `
   167  # tfsec:ignore:aws-service-abc123:exp:2221-01-02
   168  resource "bad" "my-rule" {
   169  	
   170  }
   171  `, assertLength: 0},
   172  		{name: "IgnoreForImpliedIAMResource", inputOptions: `
   173  terraform {
   174  required_version = "~> 1.1.6"
   175  
   176  required_providers {
   177  aws = {
   178  source  = "hashicorp/aws"
   179  version = "~> 3.48"
   180  }
   181  }
   182  }
   183  
   184  # Retrieve an IAM group defined outside of this Terraform config.
   185  
   186  # tfsec:ignore:aws-iam-enforce-mfa
   187  data "aws_iam_group" "externally_defined_group" {
   188  group_name = "group-name" # tfsec:ignore:aws-iam-enforce-mfa
   189  }
   190  
   191  # Create an IAM policy and attach it to the group.
   192  
   193  # tfsec:ignore:aws-iam-enforce-mfa
   194  resource "aws_iam_policy" "test_policy" {
   195  name   = "test-policy" # tfsec:ignore:aws-iam-enforce-mfa
   196  policy = data.aws_iam_policy_document.test_policy.json # tfsec:ignore:aws-iam-enforce-mfa
   197  }
   198  
   199  # tfsec:ignore:aws-iam-enforce-mfa
   200  resource "aws_iam_group_policy_attachment" "test_policy_attachment" {
   201  group      = data.aws_iam_group.externally_defined_group.group_name # tfsec:ignore:aws-iam-enforce-mfa
   202  policy_arn = aws_iam_policy.test_policy.arn # tfsec:ignore:aws-iam-enforce-mfa
   203  }
   204  
   205  # tfsec:ignore:aws-iam-enforce-mfa
   206  data "aws_iam_policy_document" "test_policy" {
   207  statement {
   208  sid = "PublishToCloudWatch" # tfsec:ignore:aws-iam-enforce-mfa
   209  actions = [
   210  "cloudwatch:PutMetricData", # tfsec:ignore:aws-iam-enforce-mfa
   211  ]
   212  resources = ["*"] # tfsec:ignore:aws-iam-enforce-mfa
   213  }
   214  }
   215  `, assertLength: 0},
   216  		{name: "TrivyIgnoreAll", inputOptions: `
   217  resource "bad" "my-rule" {
   218      secure = false // trivy:ignore:*
   219  }
   220  `, assertLength: 0},
   221  		{name: "TrivyIgnoreLineAboveTheBlock", inputOptions: `
   222  // trivy:ignore:*
   223  resource "bad" "my-rule" {
   224     secure = false 
   225  }
   226  `, assertLength: 0},
   227  		{name: "TrivyIgnoreLineAboveTheBlockMatchingParamBool", inputOptions: `
   228  // trivy:ignore:*[secure=false]
   229  resource "bad" "my-rule" {
   230     secure = false
   231  }
   232  `, assertLength: 0},
   233  		{name: "TrivyIgnoreLineAboveTheBlockNotMatchingParamBool", inputOptions: `
   234  // trivy:ignore:*[secure=true]
   235  resource "bad" "my-rule" {
   236     secure = false 
   237  }
   238  `, assertLength: 1},
   239  		{name: "TrivyIgnoreLineAboveTheBlockMatchingParamString", inputOptions: `
   240  // trivy:ignore:*[name=myrule]
   241  resource "bad" "my-rule" {
   242      name = "myrule"
   243      secure = false
   244  }
   245  `, assertLength: 0},
   246  		{name: "TrivyIgnoreLineAboveTheBlockNotMatchingParamString", inputOptions: `
   247  // trivy:ignore:*[name=myrule2]
   248  resource "bad" "my-rule" {
   249      name = "myrule"
   250      secure = false 
   251  }
   252  `, assertLength: 1},
   253  		{name: "TrivyIgnoreLineAboveTheBlockMatchingParamInt", inputOptions: `
   254  // trivy:ignore:*[port=123]
   255  resource "bad" "my-rule" {
   256     secure = false
   257     port = 123
   258  }
   259  `, assertLength: 0},
   260  		{name: "TrivyIgnoreLineAboveTheBlockNotMatchingParamInt", inputOptions: `
   261  // trivy:ignore:*[port=456]
   262  resource "bad" "my-rule" {
   263     secure = false 
   264     port = 123
   265  }
   266  `, assertLength: 1},
   267  		{name: "TrivyIgnoreLineStackedAboveTheBlock", inputOptions: `
   268  // trivy:ignore:*
   269  // trivy:ignore:a
   270  // trivy:ignore:b
   271  // trivy:ignore:c
   272  // trivy:ignore:d
   273  resource "bad" "my-rule" {
   274     secure = false 
   275  }
   276  `, assertLength: 0},
   277  		{name: "TrivyIgnoreLineStackedAboveTheBlockWithoutMatch", inputOptions: `
   278  #trivy:ignore:*
   279  
   280  #trivy:ignore:x
   281  #trivy:ignore:a
   282  #trivy:ignore:b
   283  #trivy:ignore:c
   284  #trivy:ignore:d
   285  resource "bad" "my-rule" {
   286     secure = false 
   287  }
   288  `, assertLength: 1},
   289  		{name: "TrivyIgnoreLineStackedAboveTheBlockWithHashesWithoutSpaces", inputOptions: `
   290  #trivy:ignore:*
   291  #trivy:ignore:a
   292  #trivy:ignore:b
   293  #trivy:ignore:c
   294  #trivy:ignore:d
   295  resource "bad" "my-rule" {
   296     secure = false 
   297  }
   298  `, assertLength: 0},
   299  		{name: "TrivyIgnoreLineStackedAboveTheBlockWithoutSpaces", inputOptions: `
   300  //trivy:ignore:*
   301  //trivy:ignore:a
   302  //trivy:ignore:b
   303  //trivy:ignore:c
   304  //trivy:ignore:d
   305  resource "bad" "my-rule" {
   306     secure = false 
   307  }
   308  `, assertLength: 0},
   309  		{name: "TrivyIgnoreLineAboveTheLine", inputOptions: `
   310  resource "bad" "my-rule" {
   311  	# trivy:ignore:aws-service-abc123
   312      secure = false
   313  }
   314  `, assertLength: 0},
   315  		{name: "TrivyIgnoreWithExpDateIfDateBreachedThenDontIgnore", inputOptions: `
   316  resource "bad" "my-rule" {
   317      secure = false # trivy:ignore:aws-service-abc123:exp:2000-01-02
   318  }
   319  `, assertLength: 1},
   320  		{name: "TrivyIgnoreWithExpDateIfDateNotBreachedThenIgnoreIgnore", inputOptions: `
   321  resource "bad" "my-rule" {
   322      secure = false # trivy:ignore:aws-service-abc123:exp:2221-01-02
   323  }
   324  `, assertLength: 0},
   325  		{name: "TrivyIgnoreWithExpDateIfDateInvalidThenDropTheIgnore", inputOptions: `
   326  resource "bad" "my-rule" {
   327     secure = false # trivy:ignore:aws-service-abc123:exp:2221-13-02
   328  }
   329  `, assertLength: 1},
   330  		{name: "TrivyIgnoreAboveResourceBlockWithExpDateIfDateNotBreachedThenIgnoreIgnore", inputOptions: `
   331  #trivy:ignore:aws-service-abc123:exp:2221-01-02
   332  resource "bad" "my-rule" {
   333  }
   334  `, assertLength: 0},
   335  		{name: "TrivyIgnoreAboveResourceBlockWithExpDateAndMultipleIgnoresIfDateNotBreachedThenIgnoreIgnore", inputOptions: `
   336  # trivy:ignore:aws-service-abc123:exp:2221-01-02
   337  resource "bad" "my-rule" {
   338  	
   339  }
   340  `, assertLength: 0},
   341  		{name: "TrivyIgnoreForImpliedIAMResource", inputOptions: `
   342  terraform {
   343  required_version = "~> 1.1.6"
   344  
   345  required_providers {
   346  aws = {
   347  source  = "hashicorp/aws"
   348  version = "~> 3.48"
   349  }
   350  }
   351  }
   352  
   353  # Retrieve an IAM group defined outside of this Terraform config.
   354  
   355  # trivy:ignore:aws-iam-enforce-mfa
   356  data "aws_iam_group" "externally_defined_group" {
   357  group_name = "group-name" # trivy:ignore:aws-iam-enforce-mfa
   358  }
   359  
   360  # Create an IAM policy and attach it to the group.
   361  
   362  # trivy:ignore:aws-iam-enforce-mfa
   363  resource "aws_iam_policy" "test_policy" {
   364  name   = "test-policy" # trivy:ignore:aws-iam-enforce-mfa
   365  policy = data.aws_iam_policy_document.test_policy.json # trivy:ignore:aws-iam-enforce-mfa
   366  }
   367  
   368  # trivy:ignore:aws-iam-enforce-mfa
   369  resource "aws_iam_group_policy_attachment" "test_policy_attachment" {
   370  group      = data.aws_iam_group.externally_defined_group.group_name # trivy:ignore:aws-iam-enforce-mfa
   371  policy_arn = aws_iam_policy.test_policy.arn # trivy:ignore:aws-iam-enforce-mfa
   372  }
   373  
   374  # trivy:ignore:aws-iam-enforce-mfa
   375  data "aws_iam_policy_document" "test_policy" {
   376  statement {
   377  sid = "PublishToCloudWatch" # trivy:ignore:aws-iam-enforce-mfa
   378  actions = [
   379  "cloudwatch:PutMetricData", # trivy:ignore:aws-iam-enforce-mfa
   380  ]
   381  resources = ["*"] # trivy:ignore:aws-iam-enforce-mfa
   382  }
   383  }
   384  `, assertLength: 0}}
   385  
   386  	reg := rules.Register(exampleRule)
   387  	defer rules.Deregister(reg)
   388  
   389  	for _, tc := range testCases {
   390  		t.Run(tc.name, func(t *testing.T) {
   391  			results := scanHCL(t, tc.inputOptions)
   392  			assert.Len(t, results.GetFailed(), tc.assertLength)
   393  		})
   394  	}
   395  }
   396  
   397  func Test_IgnoreIgnoreWithExpiryAndWorkspaceAndWorkspaceSupplied(t *testing.T) {
   398  	reg := rules.Register(exampleRule)
   399  	defer rules.Deregister(reg)
   400  
   401  	results := scanHCLWithWorkspace(t, `
   402  # tfsec:ignore:aws-service-abc123:exp:2221-01-02:ws:testworkspace
   403  resource "bad" "my-rule" {
   404  }
   405  `, "testworkspace")
   406  	assert.Len(t, results.GetFailed(), 0)
   407  }
   408  
   409  func Test_IgnoreInline(t *testing.T) {
   410  	reg := rules.Register(exampleRule)
   411  	defer rules.Deregister(reg)
   412  
   413  	results := scanHCL(t, fmt.Sprintf(`
   414  	resource "bad" "sample" {
   415  		  secure = false # tfsec:ignore:%s
   416  	}
   417  	  `, exampleRule.LongID()))
   418  	assert.Len(t, results.GetFailed(), 0)
   419  }
   420  
   421  func Test_IgnoreIgnoreWithExpiryAndWorkspaceButWrongWorkspaceSupplied(t *testing.T) {
   422  	reg := rules.Register(exampleRule)
   423  	defer rules.Deregister(reg)
   424  
   425  	results := scanHCLWithWorkspace(t, `
   426  # tfsec:ignore:aws-service-abc123:exp:2221-01-02:ws:otherworkspace
   427  resource "bad" "my-rule" {
   428  	
   429  }
   430  `, "testworkspace")
   431  	assert.Len(t, results.GetFailed(), 1)
   432  }
   433  
   434  func Test_IgnoreWithAliasCodeStillIgnored(t *testing.T) {
   435  	reg := rules.Register(exampleRule)
   436  	defer rules.Deregister(reg)
   437  
   438  	results := scanHCLWithWorkspace(t, `
   439  # tfsec:ignore:aws-other-abc123
   440  resource "bad" "my-rule" {
   441  	
   442  }
   443  `, "testworkspace")
   444  	assert.Len(t, results.GetFailed(), 0)
   445  }
   446  
   447  func Test_TrivyIgnoreIgnoreWithExpiryAndWorkspaceAndWorkspaceSupplied(t *testing.T) {
   448  	reg := rules.Register(exampleRule)
   449  	defer rules.Deregister(reg)
   450  
   451  	results := scanHCLWithWorkspace(t, `
   452  # trivy:ignore:aws-service-abc123:exp:2221-01-02:ws:testworkspace
   453  resource "bad" "my-rule" {
   454  }
   455  `, "testworkspace")
   456  	assert.Len(t, results.GetFailed(), 0)
   457  }
   458  
   459  func Test_TrivyIgnoreIgnoreWithExpiryAndWorkspaceButWrongWorkspaceSupplied(t *testing.T) {
   460  	reg := rules.Register(exampleRule)
   461  	defer rules.Deregister(reg)
   462  
   463  	results := scanHCLWithWorkspace(t, `
   464  # trivy:ignore:aws-service-abc123:exp:2221-01-02:ws:otherworkspace
   465  resource "bad" "my-rule" {
   466  	
   467  }
   468  `, "testworkspace")
   469  	assert.Len(t, results.GetFailed(), 1)
   470  }
   471  
   472  func Test_TrivyIgnoreWithAliasCodeStillIgnored(t *testing.T) {
   473  	reg := rules.Register(exampleRule)
   474  	defer rules.Deregister(reg)
   475  
   476  	results := scanHCLWithWorkspace(t, `
   477  # trivy:ignore:aws-other-abc123
   478  resource "bad" "my-rule" {
   479  	
   480  }
   481  `, "testworkspace")
   482  	assert.Len(t, results.GetFailed(), 0)
   483  }
   484  
   485  func Test_TrivyIgnoreInline(t *testing.T) {
   486  	reg := rules.Register(exampleRule)
   487  	defer rules.Deregister(reg)
   488  
   489  	results := scanHCL(t, fmt.Sprintf(`
   490  	resource "bad" "sample" {
   491  		  secure = false # trivy:ignore:%s
   492  	}
   493  	  `, exampleRule.LongID()))
   494  	assert.Len(t, results.GetFailed(), 0)
   495  }
   496  
   497  func Test_IgnoreInlineByAVDID(t *testing.T) {
   498  	testCases := []struct {
   499  		input string
   500  	}{
   501  		{
   502  			input: `
   503  	resource "bad" "sample" {
   504  		  secure = false # tfsec:ignore:%s
   505  	}
   506  	  `,
   507  		},
   508  		{
   509  			input: `
   510  	resource "bad" "sample" {
   511  		  secure = false # trivy:ignore:%s
   512  	}
   513  	  `,
   514  		},
   515  	}
   516  
   517  	for _, tc := range testCases {
   518  		tc := tc
   519  		for _, id := range []string{exampleRule.AVDID, strings.ToLower(exampleRule.AVDID), exampleRule.ShortCode, exampleRule.LongID()} {
   520  			id := id
   521  			t.Run("", func(t *testing.T) {
   522  				reg := rules.Register(exampleRule)
   523  				defer rules.Deregister(reg)
   524  				results := scanHCL(t, fmt.Sprintf(tc.input, id))
   525  				assert.Len(t, results.GetFailed(), 0)
   526  			})
   527  		}
   528  	}
   529  }