Skip to content

Commit

Permalink
feat: add preconfigured_waf_config_exclusions to allow multiple exclu…
Browse files Browse the repository at this point in the history
…sions
  • Loading branch information
imrannayer committed Mar 5, 2024
1 parent a8948df commit 8ab0306
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 175 deletions.
285 changes: 187 additions & 98 deletions README.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions docs/upgrading_to_v2.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Upgrading to v2.1.0

The v2.1 release contains backwards-compatible. `preconfigured_waf_config_exclusion` is obsolete and will be removed in next major version.

## Preconfigured WAF Config
:bangbang: **NOTE:** `preconfigured_waf_config_exclusion` in `pre_configured_rules` and `custom_rules` is obsolete and available for backward compatibility only. Use `preconfigured_waf_config_exclusions` which allows multiple exclusions. They are mutually exclusive.

If you are migrating from `preconfigured_waf_config_exclusion` to `preconfigured_waf_config_exclusions` first remove `preconfigured_waf_config_exclusion` and apply the code, then add exclusions using `preconfigured_waf_config_exclusions`
97 changes: 48 additions & 49 deletions examples/simple-example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -40,66 +40,65 @@ module "cloud_armor" {
priority = 1
target_rule_set = "sqli-v33-stable"
sensitivity_level = 4
description = "sqli-v33-stable Sensitivity Level 4 and 2 preconfigured_waf_config_exclusions"

# 2 exclusions
preconfigured_waf_config_exclusions = {
exclusion_1 = {
target_rule_set = "sqli-v33-stable"
target_rule_ids = ["owasp-crs-v030301-id942120-sqli", "owasp-crs-v030301-id942130-sqli"]
request_cookie = [
{
operator = "STARTS_WITH"
value = "abc"
}
]
request_header = [
{
operator = "STARTS_WITH"
value = "xyz"
},
{
operator = "STARTS_WITH"
value = "uvw"
}
]
}
exclusion_2 = {
target_rule_set = "sqli-v33-stable"
target_rule_ids = ["owasp-crs-v030301-id942150-sqli", "owasp-crs-v030301-id942180-sqli"]
request_header = [
{
operator = "STARTS_WITH"
value = "lmn"
},
{
operator = "ENDS_WITH"
value = "opq"
}
]
request_uri = [
{
operator = "CONTAINS"
value = "https://hashicorp.com"
},
{
operator = "CONTAINS"
value = "https://xyz.com"
},
]
}

preconfigured_waf_config_exclusion = {
target_rule_set = "sqli-v33-stable"
request_cookie = [
{
operator = "EQUALS_ANY"
},
{
operator = "STARTS_WITH"
value = "abc"
}
]
request_header = [
{
operator = "STARTS_WITH"
value = "xyz"
},
{
operator = "STARTS_WITH"
value = "uvw"
}
]
}

}

"xss-stable_level_2_with_exclude" = {
action = "deny(502)"
priority = 2
description = "XSS Sensitivity Level 2 with excluded rules"
preview = true
target_rule_set = "xss-v33-stable"
sensitivity_level = 2
exclude_target_rule_ids = ["owasp-crs-v030301-id941380-xss", "owasp-crs-v030301-id941280-xss"]

preconfigured_waf_config_exclusion = {
target_rule_set = "xss-v33-stable"
target_rule_ids = ["owasp-crs-v030301-id941140-xss", "owasp-crs-v030301-id941270-xss"]
request_header = [
{
operator = "STARTS_WITH"
value = "abc"
},
{
operator = "ENDS_WITH"
value = "xyz"
}
]
request_uri = [
{
operator = "CONTAINS"
value = "https://hashicorp.com"
},
{
operator = "CONTAINS"
value = "https://xyz.com"
},
]
}

}

"php-stable_level_0_with_include" = {
Expand Down
87 changes: 87 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,50 @@ resource "google_compute_security_policy" "policy" {
}
}
}

# Optional preconfigured_waf_config Block if preconfigured_waf_config_exclusion is provided
dynamic "preconfigured_waf_config" {
for_each = rule.value.preconfigured_waf_config_exclusions == null ? [] : ["preconfigured_waf_config_exclusions"] #rule.value.preconfigured_waf_config_exclusions
content {
dynamic "exclusion" {
for_each = rule.value.preconfigured_waf_config_exclusions
content {
target_rule_set = exclusion.value.target_rule_set
target_rule_ids = exclusion.value.target_rule_ids
dynamic "request_header" {
for_each = exclusion.value.request_header == null ? {} : { for x in exclusion.value.request_header : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_header.value.operator
value = request_header.value.operator == "EQUALS_ANY" ? null : request_header.value.value
}
}
dynamic "request_cookie" {
for_each = exclusion.value.request_cookie == null ? {} : { for x in exclusion.value.request_cookie : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_cookie.value.operator
value = request_cookie.value.operator == "EQUALS_ANY" ? null : request_cookie.value.value
}
}
dynamic "request_uri" {
for_each = exclusion.value.request_uri == null ? {} : { for x in exclusion.value.request_uri : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_uri.value.operator
value = request_uri.value.operator == "EQUALS_ANY" ? null : request_uri.value.value
}
}
dynamic "request_query_param" {
for_each = exclusion.value.request_query_param == null ? {} : { for x in exclusion.value.request_query_param : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_query_param.value.operator
value = request_query_param.value.operator == "EQUALS_ANY" ? null : request_query_param.value.value
}
}
}
}

}
}

}
}

Expand Down Expand Up @@ -406,6 +450,49 @@ resource "google_compute_security_policy" "policy" {
}
}

# Optional preconfigured_waf_config Block if preconfigured_waf_config_exclusion is provided
dynamic "preconfigured_waf_config" {
for_each = rule.value.preconfigured_waf_config_exclusions == null ? [] : ["preconfigured_waf_config_exclusions"] #rule.value.preconfigured_waf_config_exclusions
content {
dynamic "exclusion" {
for_each = rule.value.preconfigured_waf_config_exclusions
content {
target_rule_set = exclusion.value.target_rule_set
target_rule_ids = exclusion.value.target_rule_ids
dynamic "request_header" {
for_each = exclusion.value.request_header == null ? {} : { for x in exclusion.value.request_header : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_header.value.operator
value = request_header.value.operator == "EQUALS_ANY" ? null : request_header.value.value
}
}
dynamic "request_cookie" {
for_each = exclusion.value.request_cookie == null ? {} : { for x in exclusion.value.request_cookie : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_cookie.value.operator
value = request_cookie.value.operator == "EQUALS_ANY" ? null : request_cookie.value.value
}
}
dynamic "request_uri" {
for_each = exclusion.value.request_uri == null ? {} : { for x in exclusion.value.request_uri : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_uri.value.operator
value = request_uri.value.operator == "EQUALS_ANY" ? null : request_uri.value.value
}
}
dynamic "request_query_param" {
for_each = exclusion.value.request_query_param == null ? {} : { for x in exclusion.value.request_query_param : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_query_param.value.operator
value = request_query_param.value.operator == "EQUALS_ANY" ? null : request_query_param.value.value
}
}
}
}

}
}


}
}
Expand Down
43 changes: 20 additions & 23 deletions test/integration/simple-example/simple_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,38 +46,35 @@ func TestSimpleExample(t *testing.T) {
for _, sp := range spRule1.Array() {
assert.Equal("deny(502)", sp.Get("action").String(), "priority 1 rule has expected action")
assert.Equal("evaluatePreconfiguredWaf('sqli-v33-stable', {'sensitivity': 4})", sp.Get("match.expr.expression").String(), "priority 1 rule has expected rule expression")
assert.Empty(sp.Get("description").String(), "priority 2 rule has expected description")
assert.Equal("sqli-v33-stable Sensitivity Level 4 and 2 preconfigured_waf_config_exclusions", sp.Get("description").String(), "priority 1 rule has expected description")
assert.False(sp.Get("preview").Bool(), "priority 1 rule Preview is set to False")
for _, pce := range sp.Get("preconfiguredWafConfig.exclusions").Array() {
assert.Equal("EQUALS_ANY", pce.Get("requestCookiesToExclude").Array()[0].Get("op").String(), "priority 1 rule has expected requestCookiesToExclude")
assert.Equal("STARTS_WITH", pce.Get("requestCookiesToExclude").Array()[1].Get("op").String(), "priority 1 rule has expected requestCookiesToExclude")
assert.Equal("abc", pce.Get("requestCookiesToExclude").Array()[1].Get("val").String(), "priority 1 rule has expected requestCookiesToExclude")

assert.Equal("STARTS_WITH", pce.Get("requestHeadersToExclude").Array()[0].Get("op").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("STARTS_WITH", pce.Get("requestHeadersToExclude").Array()[1].Get("op").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("xyz", pce.Get("requestHeadersToExclude").Array()[1].Get("val").String(), "priority 1 rule has expected requestHeadersToExclude")
}

pce := sp.Get("preconfiguredWafConfig.exclusions").Array()
assert.Equal("STARTS_WITH", pce[0].Get("requestCookiesToExclude").Array()[0].Get("op").String(), "priority 1 rule has expected requestCookiesToExclude")
assert.Equal("abc", pce[0].Get("requestCookiesToExclude").Array()[0].Get("val").String(), "priority 1 rule has expected requestCookiesToExclude")
assert.Equal("STARTS_WITH", pce[0].Get("requestHeadersToExclude").Array()[0].Get("op").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("uvw", pce[0].Get("requestHeadersToExclude").Array()[0].Get("val").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("STARTS_WITH", pce[0].Get("requestHeadersToExclude").Array()[1].Get("op").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("xyz", pce[0].Get("requestHeadersToExclude").Array()[1].Get("val").String(), "priority 1 rule has expected requestHeadersToExclude")

assert.Equal("ENDS_WITH", pce[1].Get("requestHeadersToExclude").Array()[0].Get("op").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("opq", pce[1].Get("requestHeadersToExclude").Array()[0].Get("val").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("STARTS_WITH", pce[1].Get("requestHeadersToExclude").Array()[1].Get("op").String(), "priority 1 rule has expected requestHeadersToExclude")
assert.Equal("lmn", pce[1].Get("requestHeadersToExclude").Array()[1].Get("val").String(), "priority 1 rule has expected requestHeadersToExclude")

assert.Equal("CONTAINS", pce[1].Get("requestUrisToExclude").Array()[0].Get("op").String(), "priority 1 rule has expected requestUrisToExclude")
assert.Equal("https://xyz.com", pce[1].Get("requestUrisToExclude").Array()[0].Get("val").String(), "priority 1 rule has expected requestUrisToExclude")
assert.Equal("CONTAINS", pce[1].Get("requestUrisToExclude").Array()[1].Get("op").String(), "priority 1 rule has expected requestUrisToExclude")
assert.Equal("https://hashicorp.com", pce[1].Get("requestUrisToExclude").Array()[1].Get("val").String(), "priority 1 rule has expected requestUrisToExclude")
}

// Rule 2
spRule2 := gcloud.Run(t, fmt.Sprintf("compute security-policies rules describe 2 --security-policy=%s --project %s", policyName, projectId))
for _, sp := range spRule2.Array() {
assert.Equal("deny(502)", sp.Get("action").String(), "priority 2 rule has expected action")
assert.Equal("evaluatePreconfiguredWaf('xss-v33-stable', {'sensitivity': 2, 'opt_out_rule_ids': ['owasp-crs-v030301-id941380-xss','owasp-crs-v030301-id941280-xss']})", sp.Get("match.expr.expression").String(), "priority 2 rule has expected rule expression")
assert.Equal("XSS Sensitivity Level 2 with excluded rules", sp.Get("description").String(), "priority 2 rule has expected description")
assert.Empty(sp.Get("description").String(), "priority 2 rule has expected description")
assert.True(sp.Get("preview").Bool(), "priority 2 rule Preview is set to True")

for _, pce := range sp.Get("preconfiguredWafConfig.exclusions").Array() {
assert.Equal("ENDS_WITH", pce.Get("requestHeadersToExclude").Array()[0].Get("op").String(), "priority 2 rule has expected requestHeadersToExclude")
assert.Equal("STARTS_WITH", pce.Get("requestHeadersToExclude").Array()[1].Get("op").String(), "priority 2 rule has expected requestHeadersToExclude")
assert.Equal("xyz", pce.Get("requestHeadersToExclude").Array()[0].Get("val").String(), "priority 2 rule has expected requestHeadersToExclude")
assert.Equal("abc", pce.Get("requestHeadersToExclude").Array()[1].Get("val").String(), "priority 2 rule has expected requestHeadersToExclude")

assert.Equal("CONTAINS", pce.Get("requestUrisToExclude").Array()[0].Get("op").String(), "priority 2 rule has expected requestUrisToExclude")
assert.Equal("CONTAINS", pce.Get("requestUrisToExclude").Array()[1].Get("op").String(), "priority 2 rule has expected requestUrisToExclude")
assert.Equal("https://xyz.com", pce.Get("requestUrisToExclude").Array()[0].Get("val").String(), "priority 2 rule has expected requestUrisToExclude")
assert.Equal("https://hashicorp.com", pce.Get("requestUrisToExclude").Array()[1].Get("val").String(), "priority 2 rule has expected requestUrisToExclude")
}
}

spRule3 := gcloud.Run(t, fmt.Sprintf("compute security-policies rules describe 3 --security-policy=%s --project %s", policyName, projectId))
Expand Down
48 changes: 45 additions & 3 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ variable "recaptcha_redirect_site_key" {
}

variable "pre_configured_rules" {
description = "Map of pre-configured rules Sensitivity levels"
description = "Map of pre-configured rules with Sensitivity levels. preconfigured_waf_config_exclusion is obsolete and available for backward compatibility. Use preconfigured_waf_config_exclusions which allows multiple exclusions"
type = map(object({
action = string
priority = number
Expand Down Expand Up @@ -94,7 +94,28 @@ variable "pre_configured_rules" {
operator = string
value = optional(string)
})))
}), { target_rule_set = null })
}), { target_rule_set = null }) # Obsolete. Use preconfigured_waf_config_exclusions

preconfigured_waf_config_exclusions = optional(map(object({
target_rule_set = string
target_rule_ids = optional(list(string), [])
request_header = optional(list(object({
operator = string
value = optional(string)
})))
request_cookie = optional(list(object({
operator = string
value = optional(string)
})))
request_uri = optional(list(object({
operator = string
value = optional(string)
})))
request_query_param = optional(list(object({
operator = string
value = optional(string)
})))
})), null)

}))

Expand Down Expand Up @@ -184,7 +205,28 @@ variable "custom_rules" {
operator = string
value = optional(string)
})))
}), { target_rule_set = null })
}), { target_rule_set = null }) # Obsolete. Use preconfigured_waf_config_exclusions

preconfigured_waf_config_exclusions = optional(map(object({
target_rule_set = string
target_rule_ids = optional(list(string), [])
request_header = optional(list(object({
operator = string
value = optional(string)
})))
request_cookie = optional(list(object({
operator = string
value = optional(string)
})))
request_uri = optional(list(object({
operator = string
value = optional(string)
})))
request_query_param = optional(list(object({
operator = string
value = optional(string)
})))
})), null)

}))
default = {}
Expand Down
4 changes: 2 additions & 2 deletions versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ terraform {
required_providers {
google = {
source = "hashicorp/google"
version = ">= 4.79.0, < 6"
version = ">= 4.79, < 6"
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 4.79.0, < 6"
version = ">= 4.79, < 6"
}
}
provider_meta "google" {
Expand Down

0 comments on commit 8ab0306

Please sign in to comment.