Skip to content

Commit

Permalink
add support for MULTIPART_PART_HEADERS (#452)
Browse files Browse the repository at this point in the history
* add support for MULTIPART_PART_HEADERS
* Add MULTIPART_PART_HEADERS tests
  • Loading branch information
jptosso committed Oct 2, 2022
1 parent 5408fcd commit 008a3f3
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 3 deletions.
8 changes: 7 additions & 1 deletion bodyprocessors/multipart.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func (mbp *multipartBodyProcessor) ProcessRequest(reader io.Reader, collections
postCol := (collections[variables.ArgsPost]).(*collection.Map)
filesCombinedSizeCol := (collections[variables.FilesCombinedSize]).(*collection.Simple)
filesNamesCol := (collections[variables.FilesNames]).(*collection.Map)
headersNames := (collections[variables.MultipartPartHeaders]).(*collection.Map)
for {
p, err := mr.NextPart()
if err == io.EOF {
Expand All @@ -47,7 +48,12 @@ func (mbp *multipartBodyProcessor) ProcessRequest(reader io.Reader, collections
if err != nil {
return err
}

partName := p.FormName()
for key, values := range p.Header {
for _, value := range values {
headersNames.Add(partName, fmt.Sprintf("%s: %s", key, value))
}
}
// if is a file
filename := originFileName(p)
if filename != "" {
Expand Down
64 changes: 64 additions & 0 deletions bodyprocessors/multipart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,67 @@
// SPDX-License-Identifier: Apache-2.0

package bodyprocessors

import (
"strings"
"testing"

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

func TestMultipartPayload(t *testing.T) {
payload := strings.TrimSpace(`
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="text"
text default
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
Content of a.txt.
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html
<!DOCTYPE html><title>Content of a.html.</title>
-----------------------------9051914041544843365972754266--
`)
mp := &multipartBodyProcessor{}
collections := createCollections()
if err := mp.ProcessRequest(strings.NewReader(payload), collections, Options{
Mime: "multipart/form-data; boundary=---------------------------9051914041544843365972754266",
}); err != nil {
t.Fatal(err)
}
// first we validate we got the headers
headers := collections[variables.MultipartPartHeaders].(*collection.Map)
header1 := "Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\""
header2 := "Content-Type: text/html"
if h := headers.Get("file2"); len(h) == 0 {
t.Fatal("expected headers for file2")
} else {
if len(h) != 2 {
t.Fatal("expected 2 headers for file2")
}
if (h[0] != header1 && h[0] != header2) || (h[1] != header1 && h[1] != header2) {
t.Fatalf("Got invalid multipart headers")
}
}
}

func createCollections() [types.VariablesCount]collection.Collection {
collections := [types.VariablesCount]collection.Collection{}
collections[variables.Files] = collection.NewMap(variables.Files)
collections[variables.FilesTmpNames] = collection.NewMap(variables.FilesTmpNames)
collections[variables.FilesSizes] = collection.NewMap(variables.FilesSizes)
collections[variables.ArgsPost] = collection.NewMap(variables.ArgsPost)
collections[variables.FilesCombinedSize] = collection.NewSimple(variables.FilesCombinedSize)
collections[variables.FilesNames] = collection.NewMap(variables.FilesNames)
collections[variables.MultipartPartHeaders] = collection.NewMap(variables.MultipartPartHeaders)
return collections
}
1 change: 1 addition & 0 deletions internal/corazawaf/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,7 @@ type TransactionVariables struct {
XML *collection.Map
RequestXML *collection.Map
ResponseXML *collection.Map
MultipartPartHeaders *collection.Map
// Persistent variables
IP *collection.Map
// Translation Proxy Variables
Expand Down
2 changes: 2 additions & 0 deletions internal/corazawaf/waf.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ func (w *WAF) NewTransaction(ctx context.Context) *Transaction {
tx.Collections[variables.ResponseXML] = tx.Variables.ResponseXML
tx.Variables.RequestXML = collection.NewMap(variables.RequestXML)
tx.Collections[variables.RequestXML] = tx.Variables.RequestXML
tx.Variables.MultipartPartHeaders = collection.NewMap(variables.MultipartPartHeaders)
tx.Collections[variables.MultipartPartHeaders] = tx.Variables.MultipartPartHeaders

tx.Variables.ArgsCombinedSize = collection.NewCollectionSizeProxy(variables.ArgsCombinedSize, tx.Variables.ArgsGet, tx.Variables.ArgsPost)
tx.Collections[variables.ArgsCombinedSize] = tx.Variables.ArgsCombinedSize
Expand Down
5 changes: 4 additions & 1 deletion testing/engine/multipart.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ airween
`,
},
Output: profile.ExpectedOutput{
TriggeredRules: []int{100},
TriggeredRules: []int{100, 200, 250, 300},
NonTriggeredRules: []int{150, 200002},
},
},
Expand All @@ -54,6 +54,9 @@ airween
SecRequestBodyAccess On
SecRule ARGS_POST:_msg_body "Hi" "id:100, phase:2,log"
SecRule ARGS_GET:_msg_body "Hi" "id:150, phase:2,log"
SecRule ARGS:_msg_body "@rx Hi Martin," "id:200, phase:2,log"
SecRule MULTIPART_PART_HEADERS:_msg_body "Content-Disposition" "id:250, phase:2, log"
SecRule MULTIPART_PART_HEADERS "Content-Disposition" "id:300, phase:2, log"
SecRule REQBODY_ERROR "!@eq 0" \
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
`,
Expand Down
2 changes: 1 addition & 1 deletion types/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ package types

// VariablesCount contains the number of variables handled by the variables package
// It is used to create arrays of the correct size
const VariablesCount = 91
const VariablesCount = 92
3 changes: 3 additions & 0 deletions types/variables/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ const (
RequestXML
// XML is a pointer to ResponseXML
XML
// MultipartPartHeaders contains the multipart headers
MultipartPartHeaders
)

var rulemap = map[RuleVariable]string{
Expand Down Expand Up @@ -317,6 +319,7 @@ var rulemap = map[RuleVariable]string{
RequestXML: "REQUEST_XML",
ResponseXML: "RESPONSE_XML",
ResponseArgs: "RESPONSE_ARGS",
MultipartPartHeaders: "MULTIPART_PART_HEADERS",
}

var rulemapRev = map[string]RuleVariable{}
Expand Down

0 comments on commit 008a3f3

Please sign in to comment.