Skip to content

Commit

Permalink
feat(operator): New RESTPATH operator support (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
jptosso authored Jul 9, 2022
1 parent f1706cc commit a86873b
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
66 changes: 66 additions & 0 deletions operators/restpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2022 Juan Pablo Tosso
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package operators

import (
"fmt"
"regexp"
"strings"

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

var rePathTokenRe = regexp.MustCompile(`\{([^\}]+)\}`)

// @restpath takes as argument a path expression in the format
// /path/to/resource/{id}/{name}/{age}
// It will later transform the path to a regex and assign the variables to
// ARGS_PATH
type restpath struct {
re *regexp.Regexp
}

func (o *restpath) Init(options coraza.RuleOperatorOptions) error {
data := strings.ReplaceAll(options.Arguments, "/", "\\/")
for _, token := range rePathTokenRe.FindAllStringSubmatch(data, -1) {
data = strings.Replace(data, token[0], fmt.Sprintf("(?P<%s>.*)", token[1]), 1)
}
re, err := regexp.Compile(data)
o.re = re
return err
}

func (o *restpath) Evaluate(tx *coraza.Transaction, value string) bool {
// we use the re regex to match the path and match named captured groups
// to the ARGS_PATH
match := o.re.FindStringSubmatch(value)
if len(match) == 0 {
return false
}
for i, m := range o.re.SubexpNames() {
if i != 0 && m != "" {
tx.Variables.ArgsPath.SetIndex(m, 0, match[i])
}
}
return true
}

var _ coraza.RuleOperator = &restpath{}

func init() {
Register("restpath", func() coraza.RuleOperator {
return &restpath{}
})
}
41 changes: 41 additions & 0 deletions operators/restpath_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2022 Juan Pablo Tosso
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package operators

import (
"context"
"testing"

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

func TestRestPath(t *testing.T) {
waf := coraza.NewWaf()
tx := waf.NewTransaction(context.Background())
exp := "/some-random/url-{id}/{name}"
path := "/some-random/url-123/juan"
rp := restpath{}
if err := rp.Init(coraza.RuleOperatorOptions{
Arguments: exp,
}); err != nil {
t.Error(err)
}
if !rp.Evaluate(tx, path) {
t.Errorf("Expected %s to match %s", exp, path)
}
if tx.Variables.ArgsPath.Get("id")[0] != "123" {
t.Errorf("Expected 123, got %s", tx.Variables.ArgsPath.Get("id"))
}
}
1 change: 1 addition & 0 deletions transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ type TransactionVariables struct {
// Maps Variables
ArgsGet *collection.CollectionMap
ArgsPost *collection.CollectionMap
ArgsPath *collection.CollectionMap
FilesTmpNames *collection.CollectionMap
Geo *collection.CollectionMap
Files *collection.CollectionMap
Expand Down
3 changes: 3 additions & 0 deletions types/variables/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ const (
ArgsGet
// ArgsPost contains the POST (BODY) arguments
ArgsPost
// ArgsPath contains the url path parts
ArgsPath
// FilesSizes contains the sizes of the uploaded files
FilesSizes
// FilesNames contains the names of the uploaded files
Expand Down Expand Up @@ -296,6 +298,7 @@ var rulemap = map[RuleVariable]string{
Args: "ARGS",
ArgsGet: "ARGS_GET",
ArgsPost: "ARGS_POST",
ArgsPath: "ARGS_PATH",
FilesSizes: "FILES_SIZES",
FilesNames: "FILES_NAMES",
FilesTmpContent: "FILES_TMP_CONTENT",
Expand Down
4 changes: 4 additions & 0 deletions waf.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ func (w *Waf) NewTransaction(ctx context.Context) *Transaction {
tx.Collections[variables.ArgsGet] = tx.Variables.ArgsGet
tx.Variables.ArgsPost = collection.NewCollectionMap(variables.ArgsPost)
tx.Collections[variables.ArgsPost] = tx.Variables.ArgsPost
tx.Variables.ArgsPath = collection.NewCollectionMap(variables.ArgsPath)
tx.Collections[variables.ArgsPath] = tx.Variables.ArgsPath
tx.Variables.FilesSizes = collection.NewCollectionMap(variables.FilesSizes)
tx.Collections[variables.FilesSizes] = tx.Variables.FilesSizes
tx.Variables.FilesTmpContent = collection.NewCollectionMap(variables.FilesTmpContent)
Expand Down Expand Up @@ -352,13 +354,15 @@ func (w *Waf) NewTransaction(ctx context.Context) *Transaction {
variables.Args,
tx.Variables.ArgsGet,
tx.Variables.ArgsPost,
tx.Variables.ArgsPath,
)
tx.Collections[variables.Args] = tx.Variables.Args

tx.Variables.ArgsNames = collection.NewCollectionTranslationProxy(
variables.ArgsNames,
tx.Variables.ArgsGet,
tx.Variables.ArgsPost,
tx.Variables.ArgsPath,
)
tx.Collections[variables.ArgsNames] = tx.Variables.ArgsNames
tx.Variables.ArgsGetNames = collection.NewCollectionTranslationProxy(
Expand Down

0 comments on commit a86873b

Please sign in to comment.