github.com/w3security/driftctl@v0.38.0/pkg/filter/driftignore_test.go (about)

     1  package filter
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	"github.com/w3security/driftctl/enumeration/resource"
    11  )
    12  
    13  func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
    14  	tests := []struct {
    15  		name      string
    16  		resources []*resource.Resource
    17  		want      []bool
    18  		path      string
    19  		ignores   []string
    20  	}{
    21  		{
    22  			name: "drift_ignore_no_file",
    23  			resources: []*resource.Resource{
    24  				{
    25  					Type: "type1",
    26  					Id:   "id1",
    27  				},
    28  			},
    29  			want: []bool{
    30  				false,
    31  			},
    32  			path: "testdata/drift_ignore_no_file/.driftignore",
    33  		},
    34  		{
    35  			name: "drift_ignore_empty",
    36  			resources: []*resource.Resource{
    37  				{
    38  					Type: "type1",
    39  					Id:   "id1",
    40  				},
    41  			},
    42  			want: []bool{
    43  				false,
    44  			},
    45  			path: "testdata/drift_ignore_empty/.driftignore",
    46  		},
    47  		{
    48  			name: "drift_ignore_invalid_lines",
    49  			resources: []*resource.Resource{
    50  				{
    51  					Type: "type1",
    52  					Id:   "id1",
    53  				},
    54  				{
    55  					Type: "ignored_resource",
    56  					Id:   "id2",
    57  				},
    58  			},
    59  			want: []bool{
    60  				false,
    61  				true,
    62  			},
    63  			path: "testdata/drift_ignore_invalid_lines/.driftignore",
    64  		},
    65  		{
    66  			name: "drift_ignore_valid",
    67  			resources: []*resource.Resource{
    68  				{
    69  					Type: "type1",
    70  					Id:   "id1",
    71  				},
    72  				{
    73  					Type: "wildcard_resource",
    74  					Id:   "id1/with/slash",
    75  				},
    76  				{
    77  					Type: "wildcard_resource",
    78  					Id:   "id1",
    79  				},
    80  				{
    81  					Type: "wildcard_resource",
    82  					Id:   "id2",
    83  				},
    84  				{
    85  					Type: "wildcard_resource",
    86  					Id:   "id3",
    87  				},
    88  				{
    89  					Type: "ignored_resource",
    90  					Id:   "id2",
    91  				},
    92  				{
    93  					Type: "resource_type",
    94  					Id:   "id.with.dots",
    95  				},
    96  				{
    97  					Type: "resource_type",
    98  					Id:   "idwith\\",
    99  				},
   100  				{
   101  					Type: "resource_type",
   102  					Id:   "idwith\\backslashes",
   103  				},
   104  				{
   105  					Type: "resource_type",
   106  					Id:   "idwith/slashes",
   107  				},
   108  			},
   109  			want: []bool{
   110  				false,
   111  				true,
   112  				true,
   113  				true,
   114  				true,
   115  				true,
   116  				true,
   117  				true,
   118  				true,
   119  				true,
   120  			},
   121  			path: "testdata/drift_ignore_valid/.driftignore",
   122  		},
   123  		{
   124  			name: "drift_ignore_wildcard",
   125  			resources: []*resource.Resource{
   126  				{
   127  					Type: "type1",
   128  					Id:   "id1",
   129  				},
   130  				{
   131  					Type: "type2",
   132  					Id:   "id1",
   133  				},
   134  				{
   135  					Type: "type2",
   136  					Id:   "id11",
   137  				},
   138  				{
   139  					Type: "type2",
   140  					Id:   "id2",
   141  				},
   142  				{
   143  					Type: "type3",
   144  					Id:   "id100",
   145  				},
   146  				{
   147  					Type: "type3",
   148  					Id:   "id101",
   149  				},
   150  				{
   151  					Type: "type4",
   152  					Id:   "id\\WithBac*slash***\\*\\",
   153  				},
   154  			},
   155  			want: []bool{
   156  				false,
   157  				true,
   158  				true,
   159  				false,
   160  				true,
   161  				false,
   162  				true,
   163  			},
   164  			path: "testdata/drift_ignore_wildcard/.driftignore",
   165  		},
   166  		{
   167  			name: "drift_ignore_all_exclude",
   168  			resources: []*resource.Resource{
   169  				{
   170  					Type: "type1",
   171  					Id:   "id1",
   172  				},
   173  				{
   174  					Type: "type2",
   175  					Id:   "id1",
   176  				},
   177  				{
   178  					Type: "type2",
   179  					Id:   "id11",
   180  				},
   181  				{
   182  					Type: "type2",
   183  					Id:   "id2",
   184  				},
   185  				{
   186  					Type: "type3",
   187  					Id:   "id100",
   188  				},
   189  				{
   190  					Type: "type3",
   191  					Id:   "id101",
   192  				},
   193  				{
   194  					Type: "iam_user",
   195  					Id:   "id\\WithBac*slash***\\*\\",
   196  				},
   197  				{
   198  					Type: "some_type",
   199  					Id:   "idwith/slash",
   200  				},
   201  				{
   202  					Type: "some_type",
   203  					Id:   "idwith/slash/",
   204  				},
   205  			},
   206  			want: []bool{
   207  				true,
   208  				true,
   209  				true,
   210  				true,
   211  				true,
   212  				true,
   213  				false,
   214  				false,
   215  				true,
   216  			},
   217  			path: "testdata/drift_ignore_all_exclude/.driftignore",
   218  		},
   219  		{
   220  			name: "drift_ignore_all_exclude_with_ignore_patterns",
   221  			resources: []*resource.Resource{
   222  				{
   223  					Type: "type1",
   224  					Id:   "id1",
   225  				},
   226  				{
   227  					Type: "type2",
   228  					Id:   "id1",
   229  				},
   230  				{
   231  					Type: "type2",
   232  					Id:   "id11",
   233  				},
   234  				{
   235  					Type: "type2",
   236  					Id:   "id2",
   237  				},
   238  				{
   239  					Type: "type3",
   240  					Id:   "id100",
   241  				},
   242  				{
   243  					Type: "type3",
   244  					Id:   "id101",
   245  				},
   246  				{
   247  					Type: "iam_user",
   248  					Id:   "id\\WithBac*slash***\\*\\",
   249  				},
   250  				{
   251  					Type: "some_type",
   252  					Id:   "idwith/slash",
   253  				},
   254  				{
   255  					Type: "some_type",
   256  					Id:   "idwith/slash/",
   257  				},
   258  			},
   259  			want: []bool{
   260  				true,
   261  				true,
   262  				true,
   263  				true,
   264  				true,
   265  				true,
   266  				false,
   267  				false,
   268  				true,
   269  			},
   270  			path:    "testdata/drift_ignore_all/.driftignore",
   271  			ignores: []string{"*", "!iam_user.*", "!some_type.idwith/slash"},
   272  		},
   273  		{
   274  			name: "drift_ignore_none_with_ignore_patterns",
   275  			resources: []*resource.Resource{
   276  				{
   277  					Type: "aws_s3_access_point",
   278  				},
   279  			},
   280  			want: []bool{
   281  				false,
   282  			},
   283  			path:    "testdata/drift_ignore_all/.driftignore",
   284  			ignores: []string{"!*"},
   285  		},
   286  	}
   287  	for _, tt := range tests {
   288  		t.Run(tt.name, func(t *testing.T) {
   289  			cwd, _ := os.Getwd()
   290  			defer func() { _ = os.Chdir(cwd) }()
   291  
   292  			r := NewDriftIgnore(tt.path, tt.ignores...)
   293  			got := make([]bool, 0, len(tt.want))
   294  			for _, res := range tt.resources {
   295  				got = append(got, r.IsResourceIgnored(res))
   296  			}
   297  			assert.Equal(t, tt.want, got)
   298  		})
   299  	}
   300  }
   301  
   302  func TestDriftIgnore_IsFieldIgnored(t *testing.T) {
   303  
   304  	type Args struct {
   305  		Res  *resource.Resource
   306  		Path []string
   307  		Want bool
   308  	}
   309  
   310  	tests := []struct {
   311  		name    string
   312  		args    []Args
   313  		path    string
   314  		ignores []string
   315  	}{
   316  		{
   317  			name: "drift_ignore_no_file",
   318  			args: []Args{
   319  
   320  				{
   321  					Res:  &resource.Resource{Type: "type1", Id: "id1"},
   322  					Path: []string{"Id"},
   323  					Want: false,
   324  				},
   325  				{
   326  					Res:  &resource.Resource{Type: "type2", Id: "id2"},
   327  					Path: []string{"Id"},
   328  					Want: false,
   329  				},
   330  			},
   331  			path: "testdata/drift_ignore_no_file/.driftignore",
   332  		},
   333  		{
   334  			name: "drift_ignore_empty",
   335  			args: []Args{
   336  				{
   337  					Res:  &resource.Resource{Type: "type1", Id: "id1"},
   338  					Path: []string{"Id"},
   339  					Want: false,
   340  				},
   341  				{
   342  					Res:  &resource.Resource{Type: "type2", Id: "id2"},
   343  					Path: []string{"Id"},
   344  					Want: false,
   345  				},
   346  			},
   347  			path: "testdata/drift_ignore_empty/.driftignore",
   348  		},
   349  		{
   350  			name: "drift_ignore_fields",
   351  			args: []Args{
   352  				{
   353  					Res:  &resource.Resource{Type: "res_type", Id: "full_drift_ignored"},
   354  					Path: []string{"json"},
   355  					Want: true,
   356  				},
   357  				{
   358  					Res:  &resource.Resource{Type: "res_type", Id: "full_drift_ignored"},
   359  					Path: []string{"foobar"},
   360  					Want: true,
   361  				},
   362  				{
   363  					Res:  &resource.Resource{Type: "res_type", Id: "partial_drift_ignored"},
   364  					Path: []string{"json"},
   365  					Want: false,
   366  				},
   367  				{
   368  					Res:  &resource.Resource{Type: "res_type", Id: "partial_drift_ignored"},
   369  					Path: []string{"foobar"},
   370  					Want: true,
   371  				},
   372  				{
   373  					Res:  &resource.Resource{Type: "resource_type", Id: "id.with.dots"},
   374  					Path: []string{"json"},
   375  					Want: true,
   376  				},
   377  				{
   378  					Res:  &resource.Resource{Type: "resource_type", Id: "id.with.dots"},
   379  					Path: []string{"json"},
   380  					Want: true,
   381  				},
   382  				{
   383  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\"},
   384  					Path: []string{"json"},
   385  					Want: true,
   386  				},
   387  				{
   388  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\backslashes"},
   389  					Path: []string{"json"},
   390  					Want: false,
   391  				},
   392  				{
   393  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\backslashes"},
   394  					Path: []string{"foobar"},
   395  					Want: true,
   396  				},
   397  				{
   398  					Res:  &resource.Resource{Type: "res_type", Id: "wildcard_drift_ignored"},
   399  					Path: []string{"struct", "baz"},
   400  					Want: true,
   401  				},
   402  				{
   403  					Res:  &resource.Resource{Type: "res_type", Id: "wildcard_drift_ignored"},
   404  					Path: []string{"struct", "bar"},
   405  					Want: false,
   406  				},
   407  				{
   408  					Res:  &resource.Resource{Type: "res_type", Id: "endofpath_drift_ignored"},
   409  					Path: []string{"struct", "baz"},
   410  					Want: true,
   411  				},
   412  				{
   413  					Res:  &resource.Resource{Type: "res_type", Id: "endofpath_drift_ignored"},
   414  					Path: []string{"struct", "bar"},
   415  					Want: true,
   416  				},
   417  			},
   418  			path: "testdata/drift_ignore_fields/.driftignore",
   419  		},
   420  		{
   421  			name: "drift_ignore_all_exclude_field",
   422  			args: []Args{
   423  				{
   424  					Res:  &resource.Resource{Type: "res_type", Id: "full_drift_ignored"},
   425  					Path: []string{"json"},
   426  					Want: true,
   427  				},
   428  				{
   429  					Res:  &resource.Resource{Type: "res_type", Id: "full_drift_ignored"},
   430  					Path: []string{"foobar"},
   431  					Want: true,
   432  				},
   433  				{
   434  					Res:  &resource.Resource{Type: "res_type", Id: "partial_drift_ignored"},
   435  					Path: []string{"json"},
   436  					Want: true,
   437  				},
   438  				{
   439  					Res:  &resource.Resource{Type: "res_type", Id: "partial_drift_ignored"},
   440  					Path: []string{"foobar"},
   441  					Want: true,
   442  				},
   443  				{
   444  					Res:  &resource.Resource{Type: "resource_type", Id: "id.with.dots"},
   445  					Path: []string{"json"},
   446  					Want: true,
   447  				},
   448  				{
   449  					Res:  &resource.Resource{Type: "resource_type", Id: "id.with.dots"},
   450  					Path: []string{"json"},
   451  					Want: true,
   452  				},
   453  				{
   454  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\"},
   455  					Path: []string{"json"},
   456  					Want: true,
   457  				},
   458  				{
   459  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\backslashes"},
   460  					Path: []string{"json"},
   461  					Want: true,
   462  				},
   463  				{
   464  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\backslashes"},
   465  					Path: []string{"foobar"},
   466  					Want: true,
   467  				},
   468  				{
   469  					Res:  &resource.Resource{Type: "res_type", Id: "wildcard_drift_ignored"},
   470  					Path: []string{"struct", "baz"},
   471  					Want: true,
   472  				},
   473  				{
   474  					Res:  &resource.Resource{Type: "res_type", Id: "wildcard_drift_ignored"},
   475  					Path: []string{"struct", "bar"},
   476  					Want: false,
   477  				},
   478  				{
   479  					Res:  &resource.Resource{Type: "res_type", Id: "endofpath_drift_ignored"},
   480  					Path: []string{"struct", "baz"},
   481  					Want: true,
   482  				},
   483  				{
   484  					Res:  &resource.Resource{Type: "res_type", Id: "endofpath_drift_ignored"},
   485  					Path: []string{"struct", "bar"},
   486  					Want: false,
   487  				},
   488  			},
   489  			path: "testdata/drift_ignore_all_exclude_field/.driftignore",
   490  		},
   491  		{
   492  			name: "drift_ignore_all_exclude_field_with_ignore_patterns",
   493  			args: []Args{
   494  				{
   495  					Res:  &resource.Resource{Type: "res_type", Id: "full_drift_ignored"},
   496  					Path: []string{"json"},
   497  					Want: true,
   498  				},
   499  				{
   500  					Res:  &resource.Resource{Type: "res_type", Id: "full_drift_ignored"},
   501  					Path: []string{"foobar"},
   502  					Want: true,
   503  				},
   504  				{
   505  					Res:  &resource.Resource{Type: "res_type", Id: "partial_drift_ignored"},
   506  					Path: []string{"json"},
   507  					Want: true,
   508  				},
   509  				{
   510  					Res:  &resource.Resource{Type: "res_type", Id: "partial_drift_ignored"},
   511  					Path: []string{"foobar"},
   512  					Want: true,
   513  				},
   514  				{
   515  					Res:  &resource.Resource{Type: "resource_type", Id: "id.with.dots"},
   516  					Path: []string{"json"},
   517  					Want: true,
   518  				},
   519  				{
   520  					Res:  &resource.Resource{Type: "resource_type", Id: "id.with.dots"},
   521  					Path: []string{"json"},
   522  					Want: true,
   523  				},
   524  				{
   525  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\"},
   526  					Path: []string{"json"},
   527  					Want: true,
   528  				},
   529  				{
   530  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\backslashes"},
   531  					Path: []string{"json"},
   532  					Want: true,
   533  				},
   534  				{
   535  					Res:  &resource.Resource{Type: "resource_type", Id: "idwith\\backslashes"},
   536  					Path: []string{"foobar"},
   537  					Want: true,
   538  				},
   539  				{
   540  					Res:  &resource.Resource{Type: "res_type", Id: "wildcard_drift_ignored"},
   541  					Path: []string{"struct", "baz"},
   542  					Want: true,
   543  				},
   544  				{
   545  					Res:  &resource.Resource{Type: "res_type", Id: "wildcard_drift_ignored"},
   546  					Path: []string{"struct", "bar"},
   547  					Want: false,
   548  				},
   549  				{
   550  					Res:  &resource.Resource{Type: "res_type", Id: "endofpath_drift_ignored"},
   551  					Path: []string{"struct", "baz"},
   552  					Want: true,
   553  				},
   554  				{
   555  					Res:  &resource.Resource{Type: "res_type", Id: "endofpath_drift_ignored"},
   556  					Path: []string{"struct", "bar"},
   557  					Want: false,
   558  				},
   559  			},
   560  			path:    "testdata/drift_ignore_all/.driftignore",
   561  			ignores: []string{"*", "!*.bar"},
   562  		},
   563  	}
   564  
   565  	for _, tt := range tests {
   566  		t.Run(tt.name, func(t *testing.T) {
   567  			cwd, _ := os.Getwd()
   568  			defer func() { _ = os.Chdir(cwd) }()
   569  
   570  			r := NewDriftIgnore(tt.path, tt.ignores...)
   571  			for _, arg := range tt.args {
   572  				got := r.IsFieldIgnored(arg.Res, arg.Path)
   573  				if arg.Want != got {
   574  					t.Errorf("%s.%s.%s expected %v got %v", arg.Res.ResourceType(), arg.Res.ResourceId(), strings.Join(arg.Path, "."), arg.Want, got)
   575  				}
   576  			}
   577  		})
   578  	}
   579  }
   580  
   581  func TestDriftIgnore_IsTypeIgnored(t *testing.T) {
   582  	tests := []struct {
   583  		name      string
   584  		resources []*resource.Resource
   585  		want      []bool
   586  		path      string
   587  		ignores   []string
   588  	}{
   589  		{
   590  			name: "drift_ignore_type_exclude_with_child_1_nesting",
   591  			resources: []*resource.Resource{
   592  				{
   593  					Type: "aws_route",
   594  				},
   595  				{
   596  					Type: "aws_route_table",
   597  				},
   598  				{
   599  					Type: "non_ignored_type",
   600  				},
   601  				{
   602  					Type: "ignored_type",
   603  				},
   604  			},
   605  			want: []bool{
   606  				false,
   607  				false,
   608  				false,
   609  				true,
   610  			},
   611  			path: "testdata/drift_ignore_type/.driftignore_child_1",
   612  		},
   613  		{
   614  			name: "drift_ignore_type_exclude_with_child_2_nesting",
   615  			resources: []*resource.Resource{
   616  				{
   617  					Type: "non_ignored_type",
   618  				},
   619  				{
   620  					Type: "aws_iam_user",
   621  				},
   622  				{
   623  					Type: "aws_iam_user_policy",
   624  				},
   625  				{
   626  					Type: "aws_iam_user_policy_attachment",
   627  				},
   628  				{
   629  					Type: "ignored_type",
   630  				},
   631  			},
   632  			want: []bool{
   633  				false,
   634  				false,
   635  				false,
   636  				false,
   637  				true,
   638  			},
   639  			path: "testdata/drift_ignore_type/.driftignore_child_2",
   640  		},
   641  		{
   642  			name: "drift_ignore_type_exclude",
   643  			resources: []*resource.Resource{
   644  				{
   645  					Type: "type",
   646  				},
   647  				{
   648  					Type: "type_1",
   649  				},
   650  				{
   651  					Type: "type_2",
   652  				},
   653  				{
   654  					Type: "type_3",
   655  				},
   656  			},
   657  			want: []bool{
   658  				true,
   659  				false,
   660  				true,
   661  				true,
   662  			},
   663  			path: "testdata/drift_ignore_type/.driftignore",
   664  		},
   665  		{
   666  			name: "drift_ignore_non_aws_s3_resources",
   667  			resources: []*resource.Resource{
   668  				{
   669  					Type: "aws_s3_access_point",
   670  				},
   671  				{
   672  					Type: "aws_s3_bucket",
   673  				},
   674  				{
   675  					Type: "aws_s3_bucket_acl",
   676  				},
   677  				{
   678  					Type: "aws_route53_delegation_set",
   679  				},
   680  			},
   681  			want: []bool{
   682  				false,
   683  				false,
   684  				false,
   685  				true,
   686  			},
   687  			path:    "testdata/drift_ignore_all/.driftignore",
   688  			ignores: []string{"*", "!aws_s3*"},
   689  		},
   690  		{
   691  			name: "drift_ignore_non_aws_s3_and_non_route53_resources",
   692  			resources: []*resource.Resource{
   693  				{
   694  					Type: "aws_s3_access_point",
   695  				},
   696  				{
   697  					Type: "aws_s3_bucket",
   698  				},
   699  				{
   700  					Type: "aws_s3_bucket_acl",
   701  				},
   702  				{
   703  					Type: "aws_route53_delegation_set",
   704  				},
   705  			},
   706  			want: []bool{
   707  				false,
   708  				false,
   709  				false,
   710  				false,
   711  			},
   712  			path:    "testdata/drift_ignore_all/.driftignore",
   713  			ignores: []string{"*", "!aws_s3*", "!aws_route53*"},
   714  		},
   715  		{
   716  			name: "do not ignore type when one inclusion rule with resource ID exist",
   717  			resources: []*resource.Resource{
   718  				// This type should not be ignored because of `!aws_iam_policy_attachment.foo*` expression
   719  				{
   720  					Type: "aws_iam_policy_attachment",
   721  					Id:   "foobar",
   722  				},
   723  				// This type should not be ignored because `azurerm_route` type is not ignored and is a child of `azurerm_route_table`
   724  				{
   725  					Type: "azurerm_route_table",
   726  					Id:   "uselessId",
   727  				},
   728  				// This type should not be ignored because of `!azurerm_route.barfoo` expression
   729  				{
   730  					Type: "azurerm_route",
   731  					Id:   "barfoo",
   732  				},
   733  			},
   734  			want: []bool{
   735  				false,
   736  				false,
   737  				false,
   738  			},
   739  			path:    "",
   740  			ignores: []string{"*", "!aws_iam_policy_attachment.foobar", "!azurerm_route.barfoo"},
   741  		},
   742  		{
   743  			name: "ignore type wildcard while excluding one",
   744  			resources: []*resource.Resource{
   745  				{
   746  					Type: "type_ignored",
   747  				},
   748  				{
   749  					Type: "type_not_ignored",
   750  				},
   751  			},
   752  			want: []bool{
   753  				true,
   754  				false,
   755  			},
   756  			path:    "",
   757  			ignores: []string{"type_*", "!type_not_ignored"},
   758  		},
   759  	}
   760  	for _, tt := range tests {
   761  		t.Run(tt.name, func(t *testing.T) {
   762  			cwd, _ := os.Getwd()
   763  			defer func() { _ = os.Chdir(cwd) }()
   764  
   765  			r := NewDriftIgnore(tt.path, tt.ignores...)
   766  			got := make([]bool, 0, len(tt.want))
   767  			for _, res := range tt.resources {
   768  				got = append(got, r.IsTypeIgnored(resource.ResourceType(res.ResourceType())))
   769  			}
   770  			assert.Equal(t, tt.want, got)
   771  		})
   772  	}
   773  }