Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #1266 @restpath failing on query string or extra path info #1267

Merged
merged 9 commits into from
Jan 7, 2025
3 changes: 2 additions & 1 deletion internal/operators/restpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
func newRESTPath(options plugintypes.OperatorOptions) (plugintypes.Operator, 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)
data = strings.Replace(data, token[0], fmt.Sprintf("(?P<%s>[^?/]+)", token[1]), 1)

Check warning on line 32 in internal/operators/restpath.go

View check run for this annotation

Codecov / codecov/patch

internal/operators/restpath.go#L32

Added line #L32 was not covered by tests
}

re, err := memoize.Do(data, func() (interface{}, error) { return regexp.Compile(data) })
Expand All @@ -43,6 +43,7 @@
// we use the re regex to match the path and match named captured groups
// to the ARGS_PATH
match := o.re.FindStringSubmatch(value)

Check warning on line 46 in internal/operators/restpath.go

View check run for this annotation

Codecov / codecov/patch

internal/operators/restpath.go#L46

Added line #L46 was not covered by tests
cognitivegears marked this conversation as resolved.
Show resolved Hide resolved
if len(match) == 0 {
return false
}
Expand Down
115 changes: 114 additions & 1 deletion internal/operators/restpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,119 @@ func TestRestPath(t *testing.T) {
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"))
t.Errorf("Expected id to be 123, got %s", tx.Variables().ArgsPath().Get("id"))
}
if tx.Variables().ArgsPath().Get("name")[0] != "juan" {
t.Errorf("Expected name to be juan, got %s", tx.Variables().ArgsPath().Get("name"))
}
}

func TestRestPathQueryShouldNotBeGreedy(t *testing.T) {
waf := corazawaf.NewWAF()
tx := waf.NewTransaction()

exp := "/some-random/url/{id}"
testCases := map[string]string{
"/some-random/url/123?q=test": "123", // ?q=test is query info
"/some-random/url/456/test": "456", // /test is extra path info
}
cognitivegears marked this conversation as resolved.
Show resolved Hide resolved

for path, want := range testCases {

rp, err := newRESTPath(plugintypes.OperatorOptions{
Arguments: exp,
})
if 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] != want {
t.Errorf("Expected id value of %s, got %s", want, tx.Variables().ArgsPath().Get("id"))
}
}
}

func TestRestPathShouldNotBeGreedyOnMultiMatch(t *testing.T) {
waf := corazawaf.NewWAF()
tx := waf.NewTransaction()
exp := "/some-random/url-{id}/{expression}/{name}"
path := "/some-random/url-123/foo/juan?q=test"
rp, err := newRESTPath(plugintypes.OperatorOptions{
Arguments: exp,
})
if 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 id to be 123, got %s", tx.Variables().ArgsPath().Get("id"))
}
if tx.Variables().ArgsPath().Get("expression")[0] != "foo" {
t.Errorf("Expected expression to be foo, got %s", tx.Variables().ArgsPath().Get("expression"))
}
if tx.Variables().ArgsPath().Get("name")[0] != "juan" {
t.Errorf("Expected name to be juan, got %s", tx.Variables().ArgsPath().Get("name"))
}
}

func TestRestPathWithBadExpressionShouldError(t *testing.T) {
exp := "/some-random/url-{id/{name}"
_, err := newRESTPath(plugintypes.OperatorOptions{
Arguments: exp,
})
if err == nil {
t.Error("Expected error not to be nil with a bad expression")
}
}

func TestRestPathShouldNotMatchOnIncompleteURL(t *testing.T) {
waf := corazawaf.NewWAF()
tx := waf.NewTransaction()
exp := "/some-random/url-{id}/foo"
path := "/some-random/url-123/"
rp, err := newRESTPath(plugintypes.OperatorOptions{
Arguments: exp,
})
if err != nil {
t.Error(err)
}
if rp.Evaluate(tx, path) {
t.Errorf("Expected %s to NOT match %s", exp, path)
}
}

func TestRestPathShouldNotMatchOnIncompleteURLWithEndingParam(t *testing.T) {
waf := corazawaf.NewWAF()
tx := waf.NewTransaction()
exp := "/some-random/url-{id}/{name}"
path := "/some-random/url-123/"
rp, err := newRESTPath(plugintypes.OperatorOptions{
Arguments: exp,
})
if err != nil {
t.Error(err)
}
if rp.Evaluate(tx, path) {
t.Errorf("Expected %s to NOT match %s", exp, path)
}
}

func TestRestPathShouldNotMatchOnEmptyPathElement(t *testing.T) {
waf := corazawaf.NewWAF()
tx := waf.NewTransaction()
exp := "/some-random/{id}/{name}"
path := "/some-random//test"
rp, err := newRESTPath(plugintypes.OperatorOptions{
Arguments: exp,
})
if err != nil {
t.Error(err)
}
if rp.Evaluate(tx, path) {
t.Errorf("Expected %s to NOT match %s", exp, path)
}
}
Loading