Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed WAFv2 ATP response inspection parameters #31111

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/31111.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_wafv2_web_acl: Fixed error handling `response_inspection` parameters
```
21 changes: 21 additions & 0 deletions internal/acctest/acctest.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,27 @@ func MatchResourceAttrRegionalARN(resourceName, attributeName, arnService string
}
}

// MatchResourceAttrRegionalARNRegion ensures the Terraform state regexp matches a formatted ARN with the specified region
func MatchResourceAttrRegionalARNRegion(resourceName, attributeName, arnService, region string, arnResourceRegexp *regexp.Regexp) resource.TestCheckFunc {
return func(s *terraform.State) error {
arnRegexp := arn.ARN{
AccountID: AccountID(),
Partition: Partition(),
Region: region,
Resource: arnResourceRegexp.String(),
Service: arnService,
}.String()

attributeMatch, err := regexp.Compile(arnRegexp)

if err != nil {
return fmt.Errorf("unable to compile ARN regexp (%s): %w", arnRegexp, err)
}

return resource.TestMatchResourceAttr(resourceName, attributeName, attributeMatch)(s)
}
}

// MatchResourceAttrRegionalARNNoAccount ensures the Terraform state regexp matches a formatted ARN with region but without account ID
func MatchResourceAttrRegionalARNNoAccount(resourceName, attributeName, arnService string, arnResourceRegexp *regexp.Regexp) resource.TestCheckFunc {
return func(s *terraform.State) error {
Expand Down
6 changes: 3 additions & 3 deletions internal/service/wafv2/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -2309,7 +2309,7 @@ func flattenBodyContains(apiObject *wafv2.ResponseInspectionBodyContains) []inte

m := map[string]interface{}{
"failure_strings": flex.FlattenStringSet(apiObject.FailureStrings),
"succeed_strings": flex.FlattenStringSet(apiObject.SuccessStrings),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were you able to add an Account Takeover Protection rule to a WAFv2 Web ACL using success_strings instead of succeed_strings parameter? In #31106 issue I pasted the errors I get in this case, and I was able to add an ATP rule using succeed_strings successfully.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, these lines in the flatten... functions were the problem. They should have been success_... instead of succeed_... to match the names of the parameters returned by AWS

"success_strings": flex.FlattenStringSet(apiObject.SuccessStrings),
}

return []interface{}{m}
Expand All @@ -2322,7 +2322,7 @@ func flattenHeader(apiObject *wafv2.ResponseInspectionHeader) []interface{} {

m := map[string]interface{}{
"failure_values": flex.FlattenStringSet(apiObject.FailureValues),
"succeed_values": flex.FlattenStringSet(apiObject.SuccessValues),
"success_values": flex.FlattenStringSet(apiObject.SuccessValues),
}

return []interface{}{m}
Expand All @@ -2336,7 +2336,7 @@ func flattenResponseInspectionJSON(apiObject *wafv2.ResponseInspectionJson) []in
m := map[string]interface{}{
"failure_values": flex.FlattenStringSet(apiObject.FailureValues),
"identifier": aws.StringValue(apiObject.Identifier),
"succeed_values": flex.FlattenStringSet(apiObject.SuccessValues),
"success_values": flex.FlattenStringSet(apiObject.SuccessValues),
}

return []interface{}{m}
Expand Down
151 changes: 148 additions & 3 deletions internal/service/wafv2/web_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/wafv2"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
Expand All @@ -28,6 +30,7 @@ func init() {
func testAccErrorCheckSkip(t *testing.T) resource.ErrorCheckFunc {
return acctest.ErrorCheckSkipMessagesContaining(t,
"Your request contains fields that belong to a feature you are not allowed to use",
"The scope is not valid., field: SCOPE_VALUE, parameter: CLOUDFRONT",
)
}

Expand Down Expand Up @@ -2388,15 +2391,74 @@ func TestAccWAFV2WebACL_tokenDomains(t *testing.T) {
})
}

func TestAccWAFV2WebACL_CloudFrontScope(t *testing.T) {
ctx := acctest.Context(t)
var v wafv2.WebACL
webACLName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_wafv2_web_acl.test"

providers := make(map[string]*schema.Provider)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5FactoriesNamed(ctx, t, providers, acctest.ProviderName),
CheckDestroy: acctest.CheckWithNamedProviders(testAccCheckWebACLDestroyWithProvider(ctx), providers),
Steps: []resource.TestStep{
{
Config: testAccWebACLConfig_CloudFrontScope(webACLName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckWebACLExistsWithProvider(ctx, resourceName, &v, acctest.NamedProviderFunc(acctest.ProviderName, providers)),
acctest.MatchResourceAttrRegionalARNRegion(resourceName, "arn", "wafv2", testAccCloudFrontScopeRegion(), regexp.MustCompile(`global/webacl/.+$`)),
resource.TestCheckResourceAttr(resourceName, "name", webACLName),
resource.TestCheckResourceAttr(resourceName, "scope", wafv2.ScopeCloudfront),
resource.TestCheckResourceAttr(resourceName, "rule.#", "1"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{
"name": "rule-1",
"statement.#": "1",
"statement.0.managed_rule_group_statement.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.login_path": "/api/1/signin",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.request_inspection.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.request_inspection.0.password_field.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.request_inspection.0.password_field.0.identifier": "/password",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.request_inspection.0.payload_type": "JSON",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.request_inspection.0.username_field.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.request_inspection.0.username_field.0.identifier": "/username",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.response_inspection.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.response_inspection.0.body_contains.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.response_inspection.0.body_contains.0.success_strings.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.response_inspection.0.body_contains.0.success_strings.0": "Login successful",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.response_inspection.0.body_contains.0.failure_strings.#": "1",
"statement.0.managed_rule_group_statement.0.managed_rule_group_configs.0.aws_managed_rules_atp_rule_set.0.response_inspection.0.body_contains.0.failure_strings.0": "Login failed",
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateIdFunc: testAccWebACLImportStateIdFunc(resourceName),
},
},
})
}

func testAccCheckWebACLDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
return testAccCheckWebACLDestroyWithProvider(ctx)(s, acctest.Provider)
}
}

func testAccCheckWebACLDestroyWithProvider(ctx context.Context) acctest.TestCheckWithProviderFunc {
return func(s *terraform.State, provider *schema.Provider) error {
conn := provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx)

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_wafv2_web_acl" {
continue
}

conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx)

_, err := tfwafv2.FindWebACLByThreePartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"])

if tfresource.NotFound(err) {
Expand All @@ -2415,6 +2477,10 @@ func testAccCheckWebACLDestroy(ctx context.Context) resource.TestCheckFunc {
}

func testAccCheckWebACLExists(ctx context.Context, n string, v *wafv2.WebACL) resource.TestCheckFunc {
return testAccCheckWebACLExistsWithProvider(ctx, n, v, func() *schema.Provider { return acctest.Provider })
}

func testAccCheckWebACLExistsWithProvider(ctx context.Context, n string, v *wafv2.WebACL, providerF func() *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
Expand All @@ -2425,7 +2491,7 @@ func testAccCheckWebACLExists(ctx context.Context, n string, v *wafv2.WebACL) re
return fmt.Errorf("No WAFv2 WebACL ID is set")
}

conn := acctest.Provider.Meta().(*conns.AWSClient).WAFV2Conn(ctx)
conn := providerF().Meta().(*conns.AWSClient).WAFV2Conn(ctx)

output, err := tfwafv2.FindWebACLByThreePartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["name"], rs.Primary.Attributes["scope"])

Expand Down Expand Up @@ -4653,3 +4719,82 @@ resource "aws_wafv2_web_acl" "test" {
}
`, name, domain1, domain2)
}

func testAccCloudFrontScopeRegion() string {
switch acctest.Partition() {
case endpoints.AwsPartitionID:
return endpoints.UsEast1RegionID
case endpoints.AwsCnPartitionID:
return endpoints.CnNorthwest1RegionID
default:
return acctest.Region()
}
}

func testAccCloudFrontScopeProviderConfig() string {
return acctest.ConfigRegionalProvider(testAccCloudFrontScopeRegion())
}

func testAccWebACLConfig_CloudFrontScope(name string) string {
return acctest.ConfigCompose(
testAccCloudFrontScopeProviderConfig(),
fmt.Sprintf(`
resource "aws_wafv2_web_acl" "test" {
name = %[1]q
description = %[1]q
scope = "CLOUDFRONT"

default_action {
allow {}
}

rule {
name = "rule-1"
priority = 1

override_action {
count {}
}

statement {
managed_rule_group_statement {
name = "AWSManagedRulesATPRuleSet"
vendor_name = "AWS"

managed_rule_group_configs {
aws_managed_rules_atp_rule_set {
login_path = "/api/1/signin"
request_inspection {
password_field {
identifier = "/password"
}
payload_type = "JSON"
username_field {
identifier = "/username"
}
}
response_inspection {
body_contains {
success_strings = ["Login successful"]
failure_strings = ["Login failed"]
}
}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSManagedRulesATPRuleSet_json"
sampled_requests_enabled = true
}
}

visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "friendly-metric-name"
sampled_requests_enabled = false
}
}
`, name))
}