Repository: corazawaf/coraza
Pull request #619 (2023-02-08 22:57:22)

Backfills in memory limit
Currently in memory limit default value is hardcoded to 1GB which is inconvenient when we set low values for body limit e.g. in tests as we also need to set the in memory limit despite the fact it is part of the test or not.

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=619)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL) [100 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf) Report
Base: **77.85**% // Head: **77.76**% // Decreases project coverage by **`-0.10%`** :warning:
> Coverage data is based on head [(`d154e38`)](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf) compared to base [(`7c8556e`)](https://codecov.io/gh/corazawaf/coraza/commit/7c8556e67a6933f661d025caa0eff1b6e3ebaa07?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf).
> Patch coverage: 75.60% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           v3/dev     #619      +/-   ##
==========================================
- Coverage   77.85%   77.76%   -0.10%     
==========================================
  Files         144      144              
  Lines        7493     7501       +8     
==========================================
- Hits         5834     5833       -1     
- Misses       1430     1436       +6     
- Partials      229      232       +3     
```

| Flag | Coverage Δ | |
|---|---|---|
| default | `73.40% <75.60%> (-0.10%)` | :arrow_down: |
| examples | `29.69% <34.14%> (-0.27%)` | :arrow_down: |
| ftw | `54.84% <34.14%> (-0.12%)` | :arrow_down: |
| ftw-multiphase | `54.94% <34.14%> (-0.12%)` | :arrow_down: |
| tinygo | `71.43% <70.73%> (-0.10%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf) | Coverage Δ | |
|---|---|---|
| [internal/seclang/directives.go](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-aW50ZXJuYWwvc2VjbGFuZy9kaXJlY3RpdmVzLmdv) | `66.12% <47.05%> (-1.73%)` | :arrow_down: |
| [internal/corazawaf/waf.go](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-aW50ZXJuYWwvY29yYXphd2FmL3dhZi5nbw==) | `94.15% <92.85%> (+0.15%)` | :arrow_up: |
| [config.go](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-Y29uZmlnLmdv) | `58.53% <100.00%> (-1.93%)` | :arrow_down: |
| [waf.go](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-d2FmLmdv) | `63.07% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/corazawaf/coraza/pull/619?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf).

jcchavezs: Good question @anuraaga. The constant UnsetLimit was -1 and this could have
been a user input so if the user pass the value -1 we wouldn't fail and
instead proceed as unset. This is because we are now validate the waf
object after construction, before we validated before construction but only
peogramatically, if we wanted that to work also from directives we would
have need to duplicate the validation, now we only validate in a single
place.

On Thu, 9 Feb 2023, 02:12 Anuraag Agrawal, ***@***.***> wrote:

> ***@***.**** commented on this pull request.
> ------------------------------
>
> In config.go
> <https://github.com/corazawaf/coraza/pull/619#discussion_r1100861949>:
>
> > @@ -72,11 +72,7 @@ type WAFConfig interface {
>
>  // NewWAFConfig creates a new WAFConfig with the default settings.
>  func NewWAFConfig() WAFConfig {
> -	return &wafConfig{
> -		requestBodyLimit:         corazawaf.UnsetLimit,
>
> Why do we need to change from using unsets to pointer?
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/corazawaf/coraza/pull/619#pullrequestreview-1290271387>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAXOYAWU7Y7P6CFUE5CNG7TWWQ77NANCNFSM6AAAAAAUV26B6Q>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=619)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL) [100 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=619)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=619&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL) [100 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=619&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=619&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/config.go b/config.go
index 1e4b83d..04d5e86 100644
--- a/config.go
+++ b/config.go
@@ -72,11 +72,7 @@ type WAFConfig interface {
 
 // NewWAFConfig creates a new WAFConfig with the default settings.
 func NewWAFConfig() WAFConfig {
-	return &wafConfig{
-		requestBodyLimit:         corazawaf.UnsetLimit,
-		requestBodyInMemoryLimit: corazawaf.UnsetLimit,
-		responseBodyLimit:        corazawaf.UnsetLimit,
-	}
+	return &wafConfig{}
 }
 
 // AuditLogConfig controls audit logging.
@@ -109,10 +105,10 @@ type wafConfig struct {
 	rules                    []wafRule
 	auditLog                 *auditLogConfig
 	requestBodyAccess        bool
-	requestBodyLimit         int
-	requestBodyInMemoryLimit int
+	requestBodyLimit         *int
+	requestBodyInMemoryLimit *int
 	responseBodyAccess       bool
-	responseBodyLimit        int
+	responseBodyLimit        *int
 	responseBodyMimeTypes    []string
 	debugLogger              loggers.DebugLogger
 	errorCallback            func(rule types.MatchedRule)
@@ -189,19 +185,19 @@ func (c *wafConfig) clone() *wafConfig {
 
 func (c *wafConfig) WithRequestBodyLimit(limit int) WAFConfig {
 	ret := c.clone()
-	ret.requestBodyLimit = limit
+	ret.requestBodyLimit = &limit
 	return ret
 }
 
 func (c *wafConfig) WithRequestBodyInMemoryLimit(limit int) WAFConfig {
 	ret := c.clone()
-	ret.requestBodyInMemoryLimit = limit
+	ret.requestBodyInMemoryLimit = &limit
 	return ret
 }
 
 func (c *wafConfig) WithResponseBodyLimit(limit int) WAFConfig {
 	ret := c.clone()
-	ret.responseBodyLimit = limit
+	ret.responseBodyLimit = &limit
 	return ret
 }
 
diff --git a/internal/corazawaf/waf.go b/internal/corazawaf/waf.go
index 23cbf4d..1bade4d 100644
--- a/internal/corazawaf/waf.go
+++ b/internal/corazawaf/waf.go
@@ -50,7 +50,7 @@ type WAF struct {
 	RequestBodyLimit int64
 
 	// Request body in memory limit
-	RequestBodyInMemoryLimit int64
+	requestBodyInMemoryLimit *int64
 
 	// If true, transactions will have access to the response body
 	ResponseBodyAccess bool
@@ -150,9 +150,9 @@ func (w *WAF) newTransactionWithID(id string) *Transaction {
 	tx.AuditLogParts = w.AuditLogParts
 	tx.ForceRequestBodyVariable = false
 	tx.RequestBodyAccess = w.RequestBodyAccess
-	tx.RequestBodyLimit = w.RequestBodyLimit
+	tx.RequestBodyLimit = int64(w.RequestBodyLimit)
 	tx.ResponseBodyAccess = w.ResponseBodyAccess
-	tx.ResponseBodyLimit = w.ResponseBodyLimit
+	tx.ResponseBodyLimit = int64(w.ResponseBodyLimit)
 	tx.RuleEngine = w.RuleEngine
 	tx.HashEngine = false
 	tx.HashEnforcement = false
@@ -170,10 +170,16 @@ func (w *WAF) newTransactionWithID(id string) *Transaction {
 	// Always non-nil if buffers / collections were already initialized so we don't do any of them
 	// based on the presence of RequestBodyBuffer.
 	if tx.requestBodyBuffer == nil {
+		// if no requestBodyInMemoryLimit has been set we default to the
+		var requestBodyInMemoryLimit int64 = w.RequestBodyLimit
+		if w.requestBodyInMemoryLimit != nil {
+			requestBodyInMemoryLimit = int64(*w.requestBodyInMemoryLimit)
+		}
+
 		tx.requestBodyBuffer = NewBodyBuffer(types.BodyBufferOptions{
 			TmpPath:     w.TmpDir,
-			MemoryLimit: w.RequestBodyInMemoryLimit,
-			Limit:       w.ResponseBodyLimit,
+			MemoryLimit: requestBodyInMemoryLimit,
+			Limit:       w.RequestBodyLimit,
 		})
 		tx.responseBodyBuffer = NewBodyBuffer(types.BodyBufferOptions{
 			TmpPath: w.TmpDir,
@@ -243,7 +249,7 @@ func (w *WAF) SetDebugLogPath(path string) error {
 
 	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
 	if err != nil {
-		w.Logger.Error("failed to open the file: %s", err.Error())
+		w.Logger.Error("failed to open the debug log file: %s", err.Error())
 	}
 
 	w.Logger.SetOutput(f)
@@ -251,6 +257,8 @@ func (w *WAF) SetDebugLogPath(path string) error {
 	return nil
 }
 
+const _1gb = 1073741824
+
 // NewWAF creates a new WAF instance with default variables
 func NewWAF() *WAF {
 	logger := &stdDebugLogger{
@@ -267,15 +275,16 @@ func NewWAF() *WAF {
 		// Initializing pool for transactions
 		txPool: sync.NewPool(func() interface{} { return new(Transaction) }),
 		// These defaults are unavoidable as they are zero values for the variables
-		RuleEngine:               types.RuleEngineOn,
-		RequestBodyAccess:        false,
-		RequestBodyLimit:         _1gb,
-		RequestBodyInMemoryLimit: _1gb,
-		ResponseBodyAccess:       false,
-		ResponseBodyLimit:        _1gb,
-		AuditLogWriter:           logWriter,
-		Logger:                   logger,
+		RuleEngine:         types.RuleEngineOn,
+		RequestBodyAccess:  false,
+		RequestBodyLimit:   _1gb,
+		ResponseBodyAccess: false,
+		ResponseBodyLimit:  _1gb,
+		AuditLogWriter:     logWriter,
+		Logger:             logger,
+		TmpDir:             os.TempDir(),
 	}
+
 	if err := waf.SetDebugLogPath(""); err != nil {
 		fmt.Println(err)
 	}
@@ -297,11 +306,15 @@ func (w *WAF) SetErrorCallback(cb func(rule types.MatchedRule)) {
 	w.ErrorLogCb = cb
 }
 
-const (
-	_1gb       = 1073741824
-	UnsetLimit = -1
-)
+func (w *WAF) SetRequestBodyInMemoryLimit(limit int64) {
+	w.requestBodyInMemoryLimit = &limit
+}
 
+func (w *WAF) RequestBodyInMemoryLimit() *int64 {
+	return w.requestBodyInMemoryLimit
+}
+
+// Validate validates the waf after all the settings have been set.
 func (w *WAF) Validate() error {
 	if w.RequestBodyLimit <= 0 {
 		return errors.New("request body limit should be bigger than 0")
@@ -311,14 +324,14 @@ func (w *WAF) Validate() error {
 		return errors.New("request body limit should be at most 1GB")
 	}
 
-	if w.RequestBodyLimit != UnsetLimit {
-		if w.RequestBodyLimit < w.RequestBodyInMemoryLimit {
-			return fmt.Errorf("request body limit should be at least the memory limit")
+	if w.requestBodyInMemoryLimit != nil {
+		if *w.requestBodyInMemoryLimit <= 0 {
+			return errors.New("request body memory limit should be bigger than 0")
 		}
-	}
 
-	if w.RequestBodyInMemoryLimit <= 0 {
-		return errors.New("request body memory limit should be bigger than 0")
+		if w.RequestBodyLimit < *w.requestBodyInMemoryLimit {
+			return fmt.Errorf("request body limit should be at least the memory limit")
+		}
 	}
 
 	if w.ResponseBodyLimit <= 0 {
diff --git a/internal/seclang/directives.go b/internal/seclang/directives.go
index 4f81798..8c299e8 100644
--- a/internal/seclang/directives.go
+++ b/internal/seclang/directives.go
@@ -194,9 +194,12 @@ func directiveSecResponseBodyAccess(options *DirectiveOptions) error {
 // Anything over the limit will be rejected with status code 413 (Request Entity Too Large).
 // There is a hard limit of 1 GB.
 func directiveSecRequestBodyLimit(options *DirectiveOptions) error {
-	var err error
-	options.WAF.RequestBodyLimit, err = strconv.ParseInt(options.Opts, 10, 64)
-	return err
+	limit, err := strconv.ParseInt(options.Opts, 10, 64)
+	if err != nil {
+		return err
+	}
+	options.WAF.RequestBodyLimit = limit
+	return nil
 }
 
 // Description: Configures whether request bodies will be buffered and processed by Coraza.
@@ -318,9 +321,12 @@ func directiveSecResponseBodyLimitAction(options *DirectiveOptions) error {
 // This setting will not affect the responses with MIME types that are not selected for
 // buffering. There is a hard limit of 1 GB.
 func directiveSecResponseBodyLimit(options *DirectiveOptions) error {
-	var err error
-	options.WAF.ResponseBodyLimit, err = strconv.ParseInt(options.Opts, 10, 64)
-	return err
+	limit, err := strconv.ParseInt(options.Opts, 10, 64)
+	if err != nil {
+		return err
+	}
+	options.WAF.ResponseBodyLimit = limit
+	return nil
 }
 
 // Description: Controls what happens once a request body limit, configured with
@@ -349,9 +355,12 @@ func directiveSecRequestBodyLimitAction(options *DirectiveOptions) error {
 // When a `multipart/form-data` request is being processed, once the in-memory limit is reached,
 // the request body will start to be streamed into a temporary file on disk.
 func directiveSecRequestBodyInMemoryLimit(options *DirectiveOptions) error {
-	var err error
-	options.WAF.RequestBodyInMemoryLimit, err = strconv.ParseInt(options.Opts, 10, 64)
-	return err
+	limit, err := strconv.ParseInt(options.Opts, 10, 64)
+	if err != nil {
+		return err
+	}
+	options.WAF.SetRequestBodyInMemoryLimit(limit)
+	return nil
 }
 
 func directiveSecRemoteRulesFailAction(options *DirectiveOptions) error {
@@ -703,8 +712,11 @@ func directiveSecUploadKeepFiles(options *DirectiveOptions) error {
 
 func directiveSecUploadFileMode(options *DirectiveOptions) error {
 	fm, err := strconv.ParseInt(options.Opts, 8, 32)
+	if err != nil {
+		return err
+	}
 	options.WAF.UploadFileMode = fs.FileMode(fm)
-	return err
+	return nil
 }
 
 func directiveSecUploadFileLimit(options *DirectiveOptions) error {
diff --git a/waf.go b/waf.go
index be3016b..ce39840 100644
--- a/waf.go
+++ b/waf.go
@@ -76,20 +76,20 @@ func NewWAF(config WAFConfig) (WAF, error) {
 		waf.RequestBodyAccess = true
 	}
 
-	if c.requestBodyLimit != corazawaf.UnsetLimit {
-		waf.RequestBodyLimit = int64(c.requestBodyLimit)
+	if c.requestBodyLimit != nil {
+		waf.RequestBodyLimit = int64(*c.requestBodyLimit)
 	}
 
-	if c.requestBodyInMemoryLimit != corazawaf.UnsetLimit {
-		waf.RequestBodyInMemoryLimit = int64(c.requestBodyInMemoryLimit)
+	if c.requestBodyInMemoryLimit != nil {
+		waf.SetRequestBodyInMemoryLimit(int64(*c.requestBodyInMemoryLimit))
 	}
 
 	if c.responseBodyAccess {
 		waf.ResponseBodyAccess = true
 	}
 
-	if c.responseBodyLimit != corazawaf.UnsetLimit {
-		waf.ResponseBodyLimit = int64(c.responseBodyLimit)
+	if c.responseBodyLimit != nil {
+		waf.ResponseBodyLimit = int64(*c.responseBodyLimit)
 	}
 
 	if c.errorCallback != nil {


Test patch:
diff --git a/http/middleware_test.go b/http/middleware_test.go
index 3a3e350..6f0c81d 100644
--- a/http/middleware_test.go
+++ b/http/middleware_test.go
@@ -30,8 +30,8 @@ import (
 
 func TestProcessRequest(t *testing.T) {
 	req, _ := http.NewRequest("POST", "https://www.coraza.io/test", strings.NewReader("test=456"))
-	waf := corazawaf.NewWAF()
-	tx := waf.NewTransaction()
+	waf, _ := coraza.NewWAF(coraza.NewWAFConfig())
+	tx := waf.NewTransaction().(*corazawaf.Transaction)
 	if _, err := processRequest(tx, req); err != nil {
 		t.Fatal(err)
 	}
@@ -45,9 +45,9 @@ func TestProcessRequest(t *testing.T) {
 
 func TestProcessRequestEngineOff(t *testing.T) {
 	req, _ := http.NewRequest("POST", "https://www.coraza.io/test", strings.NewReader("test=456"))
-	waf := corazawaf.NewWAF()
-	waf.RuleEngine = types.RuleEngineOff
-	tx := waf.NewTransaction()
+	// TODO(jcchavezs): Shall we make RuleEngine a first class method in WAF config?
+	waf, _ := coraza.NewWAF(coraza.NewWAFConfig().WithDirectives("SecRuleEngine OFF"))
+	tx := waf.NewTransaction().(*corazawaf.Transaction)
 	if _, err := processRequest(tx, req); err != nil {
 		t.Fatal(err)
 	}
@@ -60,11 +60,9 @@ func TestProcessRequestEngineOff(t *testing.T) {
 }
 
 func TestProcessRequestMultipart(t *testing.T) {
-	waf := corazawaf.NewWAF()
-	waf.RequestBodyAccess = true
+	waf, _ := coraza.NewWAF(coraza.NewWAFConfig().WithRequestBodyAccess())
 
 	tx := waf.NewTransaction()
-	tx.RequestBodyAccess = true
 
 	req := createMultipartRequest(t)
 
@@ -127,17 +125,20 @@ func createMultipartRequest(t *testing.T) *http.Request {
 // from issue https://github.com/corazawaf/coraza/issues/159 @zpeasystart
 func TestDirectiveSecAuditLog(t *testing.T) {
 	waf := corazawaf.NewWAF()
-	p := seclang.NewParser(waf)
-	if err := p.FromString(`
+	waf.RequestBodyAccess = true
+	if err := seclang.NewParser(waf).FromString(`
 	SecRule REQUEST_FILENAME "@unconditionalMatch" "id:100, phase:2, t:none, log, setvar:'tx.count=+1',chain"
 	SecRule ARGS:username "@unconditionalMatch" "t:none, setvar:'tx.count=+2',chain"
 	SecRule ARGS:password "@unconditionalMatch" "t:none, setvar:'tx.count=+3'"
 		`); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
+	if err := waf.Validate(); err != nil {
+		t.Fatal(err)
+	}
+
 	tx := waf.NewTransaction()
 	defer tx.Close()
-	tx.RequestBodyAccess = true
 	tx.ForceRequestBodyVariable = true
 	// request
 	rdata := []string{
@@ -546,7 +547,7 @@ func TestHandlerAPI(t *testing.T) {
 		},
 	}
 
-	waf, err := coraza.NewWAF(coraza.NewWAFConfig().WithRequestBodyLimit(3).WithRequestBodyInMemoryLimit(3))
+	waf, err := coraza.NewWAF(coraza.NewWAFConfig().WithRequestBodyLimit(3))
 	if err != nil {
 		t.Fatalf("unexpected error while creating the WAF: %s", err.Error())
 	}
diff --git a/internal/corazawaf/transaction_test.go b/internal/corazawaf/transaction_test.go
index 6b7512b..fbe93fc 100644
--- a/internal/corazawaf/transaction_test.go
+++ b/internal/corazawaf/transaction_test.go
@@ -199,7 +199,6 @@ func TestWriteRequestBody(t *testing.T) {
 							waf.RuleEngine = types.RuleEngineOn
 							waf.RequestBodyAccess = true
 							waf.RequestBodyLimit = int64(testCase.requestBodyLimit)
-							waf.RequestBodyInMemoryLimit = int64(testCase.requestBodyLimit)
 							waf.RequestBodyLimitAction = testCase.requestBodyLimitAction
 
 							tx := waf.NewTransaction()
@@ -270,7 +269,6 @@ func TestWriteRequestBodyOnLimitReached(t *testing.T) {
 		waf.RuleEngine = types.RuleEngineOn
 		waf.RequestBodyAccess = true
 		waf.RequestBodyLimit = 2
-		waf.RequestBodyInMemoryLimit = 2
 		waf.RequestBodyLimitAction = tCase.requestBodyLimitAction
 
 		t.Run(tName, func(t *testing.T) {
diff --git a/internal/corazawaf/waf_test.go b/internal/corazawaf/waf_test.go
index f6af2fc..a98e0c0 100644
--- a/internal/corazawaf/waf_test.go
+++ b/internal/corazawaf/waf_test.go
@@ -70,3 +70,59 @@ func TestSetDebugLogPath(t *testing.T) {
 		})
 	}
 }
+
+func TestValidate(t *testing.T) {
+	testCases := map[string]struct {
+		customizer func(*WAF)
+		expectErr  bool
+	}{
+		"default": {
+			expectErr:  false,
+			customizer: func(w *WAF) {},
+		},
+		"request body limit less than zero": {
+			expectErr:  true,
+			customizer: func(w *WAF) { w.RequestBodyLimit = -1 },
+		},
+		"request body limit greater than 1gb": {
+			expectErr:  true,
+			customizer: func(w *WAF) { w.RequestBodyLimit = _1gb + 1 },
+		},
+		"request body in memory limit less than zero": {
+			expectErr:  true,
+			customizer: func(w *WAF) { w.SetRequestBodyInMemoryLimit(-1) },
+		},
+		"request body limit less than request body in memory limit": {
+			expectErr: true,
+			customizer: func(w *WAF) {
+				w.RequestBodyLimit = 10
+				w.SetRequestBodyInMemoryLimit(11)
+			}},
+		"response body limit less than zero": {
+			expectErr:  true,
+			customizer: func(w *WAF) { w.ResponseBodyLimit = -1 },
+		},
+		"response body limit greater than 1gb": {
+			expectErr:  true,
+			customizer: func(w *WAF) { w.ResponseBodyLimit = _1gb + 1 },
+		},
+	}
+
+	for name, tCase := range testCases {
+		t.Run(name, func(t *testing.T) {
+			waf := NewWAF()
+			tCase.customizer(waf)
+			err := waf.Validate()
+			if tCase.expectErr {
+				if err == nil {
+					t.Fatalf("expected error: %s", err.Error())
+				}
+			} else {
+				if err != nil {
+					t.Fatalf("unexpected error: %s", err.Error())
+				}
+			}
+
+		})
+	}
+}
diff --git a/internal/seclang/directives_test.go b/internal/seclang/directives_test.go
index 87e619e..871cd43 100644
--- a/internal/seclang/directives_test.go
+++ b/internal/seclang/directives_test.go
@@ -4,6 +4,7 @@
 package seclang
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/corazawaf/coraza/v3/internal/corazawaf"
@@ -203,3 +204,57 @@ func TestSecDataset(t *testing.T) {
 		t.Error("failed to add dataset")
 	}
 }
+
+var expectErrorOnDirective func(*corazawaf.WAF) bool = nil
+
+func TestDirectives(t *testing.T) {
+	type directiveCase struct {
+		opts  string
+		check func(*corazawaf.WAF) bool
+	}
+	directiveCases := map[string][]directiveCase{
+		"SecRequestBodyLimit": {
+			{"x", expectErrorOnDirective},
+			{"123", func(w *corazawaf.WAF) bool { return w.RequestBodyLimit == 123 }},
+		},
+		"SecResponseBodyLimit": {
+			{"y", expectErrorOnDirective},
+			{"123", func(w *corazawaf.WAF) bool { return w.ResponseBodyLimit == 123 }},
+		},
+		"SecRequestBodyInMemoryLimit": {
+			{"z", expectErrorOnDirective},
+			{"123", func(w *corazawaf.WAF) bool { return *(w.RequestBodyInMemoryLimit()) == 123 }},
+		},
+	}
+
+	for name, dCases := range directiveCases {
+		t.Run(name, func(t *testing.T) {
+			for _, tCase := range dCases {
+				d := directivesMap[strings.ToLower(name)]
+
+				t.Run(tCase.opts, func(t *testing.T) {
+					waf := corazawaf.NewWAF()
+
+					err := d(&DirectiveOptions{
+						Opts: tCase.opts,
+						WAF:  waf,
+					})
+
+					if tCase.check == nil {
+						if err == nil {
+							t.Error("expected error")
+						}
+					} else {
+						if err != nil {
+							t.Errorf("unexpected error: %s", err.Error())
+						}
+
+						if !tCase.check(waf) {
+							t.Errorf("check failed")
+						}
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/waf_test.go b/waf_test.go
index 59f75b4..08d52df 100644
--- a/waf_test.go
+++ b/waf_test.go
@@ -42,8 +42,8 @@ func TestRequestBodyLimit(t *testing.T) {
 	for name, tCase := range testCases {
 		t.Run(name, func(t *testing.T) {
 			cfg := NewWAFConfig().(*wafConfig)
-			cfg.requestBodyLimit = tCase.limit
-			cfg.requestBodyInMemoryLimit = tCase.inMemoryLimit
+			cfg.requestBodyLimit = &tCase.limit
+			cfg.requestBodyInMemoryLimit = &tCase.inMemoryLimit
 
 			_, err := NewWAF(cfg)
 			if tCase.expectedErr == nil {
@@ -84,7 +84,7 @@ func TestResponseBodyLimit(t *testing.T) {
 	for name, tCase := range testCases {
 		t.Run(name, func(t *testing.T) {
 			cfg := NewWAFConfig().(*wafConfig)
-			cfg.responseBodyLimit = tCase.limit
+			cfg.responseBodyLimit = &tCase.limit
 
 			_, err := NewWAF(cfg)
 			if tCase.expectedErr == nil {