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 }