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

Add support for regex_match_statement to AWS WAF v2 ACL rules #22452

Merged
merged 8 commits into from
Oct 4, 2022
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
7 changes: 7 additions & 0 deletions .changelog/22452.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_wafv2_web_acl: Add support for `regex_match_statement`
```

```release-note:enhancement
resource/aws_wafv2_rule_group: Add support for `regex_match_statement`
```
44 changes: 44 additions & 0 deletions internal/service/wafv2/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ func expandStatement(m map[string]interface{}) *wafv2.Statement {
statement.OrStatement = expandOrStatement(v.([]interface{}))
}

if v, ok := m["regex_match_statement"]; ok {
statement.RegexMatchStatement = expandRegexMatchStatement(v.([]interface{}))
}

if v, ok := m["regex_pattern_set_reference_statement"]; ok {
statement.RegexPatternSetReferenceStatement = expandRegexPatternSetReferenceStatement(v.([]interface{}))
}
Expand Down Expand Up @@ -672,6 +676,20 @@ func expandOrStatement(l []interface{}) *wafv2.OrStatement {
}
}

func expandRegexMatchStatement(l []interface{}) *wafv2.RegexMatchStatement {
if len(l) == 0 || l[0] == nil {
return nil
}

m := l[0].(map[string]interface{})

return &wafv2.RegexMatchStatement{
RegexString: aws.String(m["regex_string"].(string)),
FieldToMatch: expandFieldToMatch(m["field_to_match"].([]interface{})),
TextTransformations: expandTextTransformations(m["text_transformation"].(*schema.Set).List()),
}
}

func expandRegexPatternSetReferenceStatement(l []interface{}) *wafv2.RegexPatternSetReferenceStatement {
if len(l) == 0 || l[0] == nil {
return nil
Expand Down Expand Up @@ -893,6 +911,10 @@ func expandWebACLStatement(m map[string]interface{}) *wafv2.Statement {
statement.RateBasedStatement = expandRateBasedStatement(v.([]interface{}))
}

if v, ok := m["regex_match_statement"]; ok {
statement.RegexMatchStatement = expandRegexMatchStatement(v.([]interface{}))
}

if v, ok := m["regex_pattern_set_reference_statement"]; ok {
statement.RegexPatternSetReferenceStatement = expandRegexPatternSetReferenceStatement(v.([]interface{}))
}
Expand Down Expand Up @@ -1261,6 +1283,10 @@ func flattenStatement(s *wafv2.Statement) map[string]interface{} {
m["or_statement"] = flattenOrStatement(s.OrStatement)
}

if s.RegexMatchStatement != nil {
m["regex_match_statement"] = flattenRegexMatchStatement(s.RegexMatchStatement)
}

if s.RegexPatternSetReferenceStatement != nil {
m["regex_pattern_set_reference_statement"] = flattenRegexPatternSetReferenceStatement(s.RegexPatternSetReferenceStatement)
}
Expand Down Expand Up @@ -1544,6 +1570,20 @@ func flattenOrStatement(a *wafv2.OrStatement) interface{} {
return []interface{}{m}
}

func flattenRegexMatchStatement(r *wafv2.RegexMatchStatement) interface{} {
if r == nil {
return []interface{}{}
}

m := map[string]interface{}{
"regex_string": aws.StringValue(r.RegexString),
"field_to_match": flattenFieldToMatch(r.FieldToMatch),
"text_transformation": flattenTextTransformations(r.TextTransformations),
}

return []interface{}{m}
}

func flattenRegexPatternSetReferenceStatement(r *wafv2.RegexPatternSetReferenceStatement) interface{} {
if r == nil {
return []interface{}{}
Expand Down Expand Up @@ -1700,6 +1740,10 @@ func flattenWebACLStatement(s *wafv2.Statement) map[string]interface{} {
m["rate_based_statement"] = flattenRateBasedStatement(s.RateBasedStatement)
}

if s.RegexMatchStatement != nil {
m["regex_match_statement"] = flattenRegexMatchStatement(s.RegexMatchStatement)
}

if s.RegexPatternSetReferenceStatement != nil {
m["regex_pattern_set_reference_statement"] = flattenRegexPatternSetReferenceStatement(s.RegexPatternSetReferenceStatement)
}
Expand Down
9 changes: 6 additions & 3 deletions internal/service/wafv2/regex_pattern_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ func ResourceRegexPatternSet() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"regex_string": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(1, 200),
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 200),
validation.StringIsValidRegExp,
),
},
},
},
Expand Down
84 changes: 83 additions & 1 deletion internal/service/wafv2/rule_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,42 @@ func TestAccWAFV2RuleGroup_minimal(t *testing.T) {
})
}

func TestAccWAFV2RuleGroup_regexMatchStatement(t *testing.T) {
var v wafv2.RuleGroup
ruleGroupName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_wafv2_rule_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckScopeRegional(t) },
ErrorCheck: acctest.ErrorCheck(t, wafv2.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckRuleGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccRuleGroupConfig_regexMatchStatement(ruleGroupName),
Check: resource.ComposeTestCheckFunc(
testAccCheckRuleGroupExists(resourceName, &v),
acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)),
resource.TestCheckResourceAttr(resourceName, "rule.#", "1"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{
"statement.#": "1",
"statement.0.regex_match_statement.#": "1",
"statement.0.regex_match_statement.0.regex_string": "[a-z]([a-z0-9_-]*[a-z0-9])?",
"statement.0.regex_match_statement.0.field_to_match.#": "1",
"statement.0.regex_match_statement.0.text_transformation.#": "1",
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateIdFunc: testAccRuleGroupImportStateIdFunc(resourceName),
},
},
})
}

func TestAccWAFV2RuleGroup_regexPatternSetReferenceStatement(t *testing.T) {
var v wafv2.RuleGroup
ruleGroupName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
Expand Down Expand Up @@ -3604,14 +3640,60 @@ resource "aws_wafv2_rule_group" "test" {
`, name)
}

func testAccRuleGroupConfig_regexMatchStatement(name string) string {
return fmt.Sprintf(`
resource "aws_wafv2_rule_group" "test" {
capacity = 50
name = "%s"
scope = "REGIONAL"

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

action {
allow {}
}

statement {
regex_match_statement {
regex_string = "[a-z]([a-z0-9_-]*[a-z0-9])?"

field_to_match {
body {}
}

text_transformation {
priority = 2
type = "NONE"
}
}
}

visibility_config {
cloudwatch_metrics_enabled = false
metric_name = "friendly-rule-metric-name"
sampled_requests_enabled = false
}
}

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

func testAccRuleGroupConfig_regexPatternSetReferenceStatement(name string) string {
return fmt.Sprintf(`
resource "aws_wafv2_regex_pattern_set" "test" {
name = "regex-pattern-set-%s"
scope = "REGIONAL"

regular_expression {
regex_string = "one"
regex_string = "[a-z]([a-z0-9_-]*[a-z0-9])?"
}
}

Expand Down
28 changes: 28 additions & 0 deletions internal/service/wafv2/schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func rootStatementSchema(level int) *schema.Schema {
"label_match_statement": labelMatchStatementSchema(),
"not_statement": statementSchema(level - 1),
"or_statement": statementSchema(level - 1),
"regex_match_statement": regexMatchStatementSchema(),
"regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(),
"size_constraint_statement": sizeConstraintSchema(),
"sqli_match_statement": sqliMatchStatementSchema(),
Expand Down Expand Up @@ -95,6 +96,7 @@ func statementSchema(level int) *schema.Schema {
"label_match_statement": labelMatchStatementSchema(),
"not_statement": statementSchema(level - 1),
"or_statement": statementSchema(level - 1),
"regex_match_statement": regexMatchStatementSchema(),
"regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(),
"size_constraint_statement": sizeConstraintSchema(),
"sqli_match_statement": sqliMatchStatementSchema(),
Expand Down Expand Up @@ -122,6 +124,7 @@ func statementSchema(level int) *schema.Schema {
"geo_match_statement": geoMatchStatementSchema(),
"ip_set_reference_statement": ipSetReferenceStatementSchema(),
"label_match_statement": labelMatchStatementSchema(),
"regex_match_statement": regexMatchStatementSchema(),
"regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(),
"size_constraint_statement": sizeConstraintSchema(),
"sqli_match_statement": sqliMatchStatementSchema(),
Expand Down Expand Up @@ -246,6 +249,28 @@ func labelMatchStatementSchema() *schema.Schema {
}
}

func regexMatchStatementSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"regex_string": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 512),
validation.StringIsValidRegExp,
),
},
"field_to_match": fieldToMatchSchema(),
"text_transformation": textTransformationSchema(),
},
},
}
}

func regexPatternSetReferenceStatementSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Expand Down Expand Up @@ -452,6 +477,7 @@ func textTransformationSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeSet,
Required: true,
MinItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"priority": {
Expand Down Expand Up @@ -777,6 +803,7 @@ func webACLRootStatementSchema(level int) *schema.Schema {
"not_statement": statementSchema(level),
"or_statement": statementSchema(level),
"rate_based_statement": rateBasedStatementSchema(level),
"regex_match_statement": regexMatchStatementSchema(),
"regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(),
"rule_group_reference_statement": ruleGroupReferenceStatementSchema(),
"size_constraint_statement": sizeConstraintSchema(),
Expand Down Expand Up @@ -871,6 +898,7 @@ func scopeDownStatementSchema(level int) *schema.Schema {
"ip_set_reference_statement": ipSetReferenceStatementSchema(),
"not_statement": statementSchema(level),
"or_statement": statementSchema(level),
"regex_match_statement": regexMatchStatementSchema(),
"regex_pattern_set_reference_statement": regexPatternSetReferenceStatementSchema(),
"size_constraint_statement": sizeConstraintSchema(),
"sqli_match_statement": sqliMatchStatementSchema(),
Expand Down
44 changes: 38 additions & 6 deletions internal/service/wafv2/web_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1709,9 +1709,10 @@ func TestAccWAFV2WebACL_RateBased_maxNested(t *testing.T) {
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.#": "1",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.#": "1",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.#": "1",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.0.statement.#": "2",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.0.statement.#": "3",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.0.statement.0.regex_pattern_set_reference_statement.#": "1",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.0.statement.1.ip_set_reference_statement.#": "1",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.0.statement.1.regex_match_statement.#": "1",
"statement.0.rate_based_statement.0.scope_down_statement.0.not_statement.0.statement.0.or_statement.0.statement.2.ip_set_reference_statement.#": "1",
}),
),
},
Expand Down Expand Up @@ -1750,9 +1751,10 @@ func TestAccWAFV2WebACL_Operators_maxNested(t *testing.T) {
"statement.0.and_statement.0.statement.0.not_statement.#": "1",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.#": "1",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.#": "1",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.0.statement.#": "2",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.0.statement.#": "3",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.0.statement.0.regex_pattern_set_reference_statement.#": "1",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.0.statement.1.ip_set_reference_statement.#": "1",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.0.statement.1.regex_match_statement.#": "1",
"statement.0.and_statement.0.statement.0.not_statement.0.statement.0.or_statement.0.statement.2.ip_set_reference_statement.#": "1",
"statement.0.and_statement.0.statement.1.geo_match_statement.#": "1",
"statement.0.and_statement.0.statement.1.geo_match_statement.0.country_codes.0": "NL",
}),
Expand Down Expand Up @@ -3221,7 +3223,7 @@ resource "aws_wafv2_regex_pattern_set" "test" {
scope = "REGIONAL"

regular_expression {
regex_string = "one"
regex_string = "[a-z]([a-z0-9_-]*[a-z0-9])?"
}
}

Expand Down Expand Up @@ -3273,6 +3275,21 @@ resource "aws_wafv2_web_acl" "test" {
}
}

statement {
regex_match_statement {
regex_string = "[a-z]([a-z0-9_-]*[a-z0-9])?"

field_to_match {
uri_path {}
}

text_transformation {
type = "LOWERCASE"
priority = 1
}
}
}

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.test.arn
Expand Down Expand Up @@ -3308,7 +3325,7 @@ resource "aws_wafv2_regex_pattern_set" "test" {
scope = "REGIONAL"

regular_expression {
regex_string = "one"
regex_string = "[a-z]([a-z0-9_-]*[a-z0-9])?"
}
}

Expand Down Expand Up @@ -3357,6 +3374,21 @@ resource "aws_wafv2_web_acl" "test" {
}
}

statement {
regex_match_statement {
regex_string = "[a-z]([a-z0-9_-]*[a-z0-9])?"

field_to_match {
uri_path {}
}

text_transformation {
type = "LOWERCASE"
priority = 1
}
}
}

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.test.arn
Expand Down
Loading