Skip to content

Commit

Permalink
feat: adds build tag for case sensitive args keys.
Browse files Browse the repository at this point in the history
  • Loading branch information
jcchavezs committed May 11, 2024
1 parent 4225223 commit ed8ba00
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 144 deletions.
8 changes: 8 additions & 0 deletions internal/corazawaf/casesensitive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2024 Juan Pablo Tosso and the OWASP Coraza contributors
// SPDX-License-Identifier: Apache-2.0

//go:build coraza.rule.case_sensitive_args_keys

package corazawaf

var shouldUseCaseSensitiveNamedCollection = true
8 changes: 8 additions & 0 deletions internal/corazawaf/casesensitive_default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2024 Juan Pablo Tosso and the OWASP Coraza contributors
// SPDX-License-Identifier: Apache-2.0

//go:build !coraza.rule.case_sensitive_args_keys

package corazawaf

var shouldUseCaseSensitiveNamedCollection = false
22 changes: 22 additions & 0 deletions internal/corazawaf/rule_casesensitive_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2024 Juan Pablo Tosso and the OWASP Coraza contributors
// SPDX-License-Identifier: Apache-2.0

//go:build coraza.rule.case_sensitive_args_keys

package corazawaf

import (
"testing"

"github.com/corazawaf/coraza/v3/types/variables"
)

func TestCaseSensitiveArgsVariableKeys(t *testing.T) {
rule := NewRule()
if err := rule.AddVariable(variables.ArgsGet, "Som3ThinG", false); err != nil {
t.Error(err)
}
if rule.variables[0].KeyStr != "Som3ThinG" {
t.Error("variable key is not case insensitive")
}
}
20 changes: 0 additions & 20 deletions internal/corazawaf/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,26 +257,6 @@ func TestRuleNegativeVariablesEmtpyRule(t *testing.T) {
}
}

func TestArgsVariableKeysAreCaseSensitive(t *testing.T) {
rule := NewRule()
if err := rule.AddVariable(variables.ArgsGet, "Som3ThinG", false); err != nil {
t.Error(err)
}
if rule.variables[0].KeyStr != "Som3ThinG" {
t.Error("variable key is not case insensitive")
}
}

func TestVariableKeysAreCaseInsensitive(t *testing.T) {
rule := NewRule()
if err := rule.AddVariable(variables.RequestURI, "Som3ThinG", false); err != nil {
t.Error(err)
}
if rule.variables[0].KeyStr != "som3thing" {
t.Error("variable key is not case insensitive")
}
}

func TestVariablesRxAreCaseSensitive(t *testing.T) {
rule := NewRule()
if err := rule.AddVariable(variables.ArgsGet, "/Som3ThinG/", false); err != nil {
Expand Down
13 changes: 10 additions & 3 deletions internal/corazawaf/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -1689,11 +1689,18 @@ func NewTransactionVariables() *TransactionVariables {
// XML is a pointer to RequestXML
v.xml = v.requestXML

v.argsGet = collections.NewCaseSensitiveNamedCollection(variables.ArgsGet)
if shouldUseCaseSensitiveNamedCollection {
v.argsGet = collections.NewCaseSensitiveNamedCollection(variables.ArgsGet)
v.argsPost = collections.NewCaseSensitiveNamedCollection(variables.ArgsPost)
v.argsPath = collections.NewCaseSensitiveNamedCollection(variables.ArgsPath)
} else {
v.argsGet = collections.NewNamedCollection(variables.ArgsGet)
v.argsPost = collections.NewNamedCollection(variables.ArgsPost)
v.argsPath = collections.NewNamedCollection(variables.ArgsPath)
}

v.argsGetNames = v.argsGet.Names(variables.ArgsGetNames)
v.argsPost = collections.NewCaseSensitiveNamedCollection(variables.ArgsPost)
v.argsPostNames = v.argsPost.Names(variables.ArgsPostNames)
v.argsPath = collections.NewCaseSensitiveNamedCollection(variables.ArgsPath)
v.argsCombinedSize = collections.NewSizeCollection(variables.ArgsCombinedSize, v.argsGet, v.argsPost)
v.args = collections.NewConcatKeyed(
variables.Args,
Expand Down
133 changes: 133 additions & 0 deletions internal/seclang/rules_casesensitive_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors
// SPDX-License-Identifier: Apache-2.0

//go:build coraza.rule.case_sensitive_args_keys

package seclang

import (
"testing"

"github.com/corazawaf/coraza/v3/internal/corazawaf"
)

func TestCaseSensitiveRuleMatchRegex(t *testing.T) {
waf := corazawaf.NewWAF()
parser := NewParser(waf)
err := parser.FromString(`
SecRuleEngine On
SecRule ARGS:/^Key/ "@streq my-value" "id:1028,phase:1,deny,status:403,msg:'ARGS:key matched.'"
`)
if err != nil {
t.Error(err.Error())
}
tx := waf.NewTransaction()
tx.ProcessURI("https://asdf.com/index.php?t1=aaa&T1=zzz&t2=bbb&t3=ccc&Keyless=my-value&a=test&jsessionid=74B0CB414BD77D17B5680A6386EF1666", "GET", "HTTP/1.1")
tx.ProcessConnection("127.0.0.1", 0, "", 0)
tx.ProcessRequestHeaders()
if len(tx.MatchedRules()) != 1 {
t.Errorf("failed to match rules with %d", len(tx.MatchedRules()))
}
if tx.Interruption() == nil {
t.Fatal("failed to interrupt transaction")
}
}

func TestCaseSensitiveArguments(t *testing.T) {
waf := corazawaf.NewWAF()
rules := `SecRule ARGS:Test1 "Xyz" "id:3, phase:2, log, deny"`
parser := NewParser(waf)

err := parser.FromString(rules)
if err != nil {
t.Error()
return
}

tx := waf.NewTransaction()
tx.ProcessRequestHeaders()
tx.AddPostRequestArgument("Test1", "Xyz")
it, err := tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}
if it == nil {
t.Errorf("failed to test arguments value match: Same case argument name, %+v\n", tx.MatchedRules())
}

tx = waf.NewTransaction()
tx.ProcessRequestHeaders()
tx.AddPostRequestArgument("TEST1", "Xyz")
it, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}
if it != nil {
t.Errorf("failed to test arguments value match: argument is matching a different case, %+v\n", tx.MatchedRules())
}

tx = waf.NewTransaction()
tx.ProcessRequestHeaders()
tx.AddPostRequestArgument("Test1", "XYZ")
it, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}
if it != nil {
t.Errorf("failed to test arguments value match: argument is matching a different case, %+v\n", tx.MatchedRules())
}
}

func TestCaseSensitiveURIQueryParam(t *testing.T) {
waf := corazawaf.NewWAF()
rules := `SecRule ARGS:Test1 "@contains SQLI" "id:3, phase:2, log, pass"`
parser := NewParser(waf)

err := parser.FromString(rules)
if err != nil {
t.Error()
return
}

tx := waf.NewTransaction()
tx.ProcessURI("/url?Test1='SQLI", "POST", "HTTP/1.1")
tx.ProcessRequestHeaders()
_, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}

if len(tx.MatchedRules()) == 1 {
if len(tx.MatchedRules()[0].MatchedDatas()) != 1 {
t.Errorf("failed to test uri query param. Found matches: %d, %+v\n",
len(tx.MatchedRules()[0].MatchedDatas()), tx.MatchedRules())
}
if !isMatchData(tx.MatchedRules()[0].MatchedDatas(), "Test1") {
t.Error("Key did not match: Test1 !=", tx.MatchedRules()[0])
}
} else {
t.Errorf("failed to test uri query param: Same case arg name: %d, %+v\n",
len(tx.MatchedRules()), tx.MatchedRules())
}

tx = waf.NewTransaction()
tx.ProcessURI("/test?test1='SQLI&Test1='SQLI&TEST1='SQLI", "POST", "HTTP/1.1")
tx.ProcessRequestHeaders()
_, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}

if len(tx.MatchedRules()) == 1 {
if len(tx.MatchedRules()[0].MatchedDatas()) != 1 {
t.Errorf("failed to test uri query param. Found matches: %d, %+v\n",
len(tx.MatchedRules()[0].MatchedDatas()), tx.MatchedRules())
}
if !isMatchData(tx.MatchedRules()[0].MatchedDatas(), "Test1") {
t.Error("Key did not match: Test1 !=", tx.MatchedRules()[0])
}
} else {
t.Errorf("failed to test qparam pollution: Multiple arg different case: %d, %+v\n",
len(tx.MatchedRules()), tx.MatchedRules())
}
}
121 changes: 0 additions & 121 deletions internal/seclang/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,51 +654,6 @@ func TestArgumentNamesCaseSensitive(t *testing.T) {
*/
}

func TestArgumentsCaseSensitive(t *testing.T) {
waf := corazawaf.NewWAF()
rules := `SecRule ARGS:Test1 "Xyz" "id:3, phase:2, log, deny"`
parser := NewParser(waf)

err := parser.FromString(rules)
if err != nil {
t.Error()
return
}

tx := waf.NewTransaction()
tx.ProcessRequestHeaders()
tx.AddPostRequestArgument("Test1", "Xyz")
it, err := tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}
if it == nil {
t.Errorf("failed to test arguments value match: Same case argument name, %+v\n", tx.MatchedRules())
}

tx = waf.NewTransaction()
tx.ProcessRequestHeaders()
tx.AddPostRequestArgument("TEST1", "Xyz")
it, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}
if it != nil {
t.Errorf("failed to test arguments value match: argument is matching a different case, %+v\n", tx.MatchedRules())
}

tx = waf.NewTransaction()
tx.ProcessRequestHeaders()
tx.AddPostRequestArgument("Test1", "XYZ")
it, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}
if it != nil {
t.Errorf("failed to test arguments value match: argument is matching a different case, %+v\n", tx.MatchedRules())
}
}

func TestCookiesCaseSensitive(t *testing.T) {
waf := corazawaf.NewWAF()
rules := `SecRule REQUEST_COOKIES:Test1 "Xyz" "id:3, phase:2, log, deny"`
Expand Down Expand Up @@ -919,60 +874,6 @@ SecRule ARGS:test1 "ZZZZ" "id:4, phase:2, log, pass"`
}
}

func TestURIQueryParamCaseSensitive(t *testing.T) {
waf := corazawaf.NewWAF()
rules := `SecRule ARGS:Test1 "@contains SQLI" "id:3, phase:2, log, pass"`
parser := NewParser(waf)

err := parser.FromString(rules)
if err != nil {
t.Error()
return
}

tx := waf.NewTransaction()
tx.ProcessURI("/url?Test1='SQLI", "POST", "HTTP/1.1")
tx.ProcessRequestHeaders()
_, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}

if len(tx.MatchedRules()) == 1 {
if len(tx.MatchedRules()[0].MatchedDatas()) != 1 {
t.Errorf("failed to test uri query param. Found matches: %d, %+v\n",
len(tx.MatchedRules()[0].MatchedDatas()), tx.MatchedRules())
}
if !isMatchData(tx.MatchedRules()[0].MatchedDatas(), "Test1") {
t.Error("Key did not match: Test1 !=", tx.MatchedRules()[0])
}
} else {
t.Errorf("failed to test uri query param: Same case arg name: %d, %+v\n",
len(tx.MatchedRules()), tx.MatchedRules())
}

tx = waf.NewTransaction()
tx.ProcessURI("/test?test1='SQLI&Test1='SQLI&TEST1='SQLI", "POST", "HTTP/1.1")
tx.ProcessRequestHeaders()
_, err = tx.ProcessRequestBody()
if err != nil {
t.Error(err)
}

if len(tx.MatchedRules()) == 1 {
if len(tx.MatchedRules()[0].MatchedDatas()) != 1 {
t.Errorf("failed to test uri query param. Found matches: %d, %+v\n",
len(tx.MatchedRules()[0].MatchedDatas()), tx.MatchedRules())
}
if !isMatchData(tx.MatchedRules()[0].MatchedDatas(), "Test1") {
t.Error("Key did not match: Test1 !=", tx.MatchedRules()[0])
}
} else {
t.Errorf("failed to test qparam pollution: Multiple arg different case: %d, %+v\n",
len(tx.MatchedRules()), tx.MatchedRules())
}
}

func TestURIQueryParamNameCaseSensitive(t *testing.T) {
waf := corazawaf.NewWAF()
rules := `SecRule ARGS_NAMES "Test1" "id:3, phase:2, log, pass"`
Expand Down Expand Up @@ -1027,28 +928,6 @@ func TestURIQueryParamNameCaseSensitive(t *testing.T) {
}
}

func TestRuleMatchRegexCaseSensitivity(t *testing.T) {
waf := corazawaf.NewWAF()
parser := NewParser(waf)
err := parser.FromString(`
SecRuleEngine On
SecRule ARGS:/^Key/ "@streq my-value" "id:1028,phase:1,deny,status:403,msg:'ARGS:key matched.'"
`)
if err != nil {
t.Error(err.Error())
}
tx := waf.NewTransaction()
tx.ProcessURI("https://asdf.com/index.php?t1=aaa&T1=zzz&t2=bbb&t3=ccc&Keyless=my-value&a=test&jsessionid=74B0CB414BD77D17B5680A6386EF1666", "GET", "HTTP/1.1")
tx.ProcessConnection("127.0.0.1", 0, "", 0)
tx.ProcessRequestHeaders()
if len(tx.MatchedRules()) != 1 {
t.Errorf("failed to match rules with %d", len(tx.MatchedRules()))
}
if tx.Interruption() == nil {
t.Fatal("failed to interrupt transaction")
}
}

func isMatchData(mds []types.MatchData, key string) (result bool) {
result = false
for _, m := range mds {
Expand Down
4 changes: 4 additions & 0 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ func Test() error {
return err
}

if err := sh.RunV("go", "test", "-tags=coraza.rule.case_sensitive_args_keys", "-run=^TestCaseSensitive", "./..."); err != nil {
return err
}

return nil
}

Expand Down

0 comments on commit ed8ba00

Please sign in to comment.