Skip to content

Commit

Permalink
Report errors when parsing EQL conditions (#2617)
Browse files Browse the repository at this point in the history
  • Loading branch information
faec authored May 9, 2023
1 parent 56de291 commit a4234ac
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
24 changes: 24 additions & 0 deletions internal/pkg/eql/eql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ func (s *testVarStore) Lookup(v string) (interface{}, bool) {
return val, ok
}

func TestEqlNewReportsBadSyntax(t *testing.T) {
// Some malformed antlr expressions can produce an error when evaluated
// because they cause a nil pointer reference or similar unhelpful
// error. These test cases confirm that eql.New reports these errors
// during the initial parsing of the expression, so things don't get
// that far.
testCases := []string{
"asdf",
"${***}",
"${",
"{}{}{}",
"1+=2",
"1.23f == ''",
"${asdf}...",
}
for _, expression := range testCases {
_, err := New(expression)
assert.Error(t, err, "malformed EQL expression \"%v\" should produce an error", expression)
}
}

func TestEql(t *testing.T) {
testcases := []struct {
expression string
Expand Down Expand Up @@ -305,6 +326,9 @@ func TestEql(t *testing.T) {
{expression: "length('hello')", err: true},
{expression: "length()", err: true},
{expression: "donotexist()", err: true},
{expression: "${***} != ${~~~}", err: true},
{expression: "false asdf!@#$", err: true},
{expression: "length('something' 345) > 1000", err: true},
}

store := &testVarStore{
Expand Down
33 changes: 33 additions & 0 deletions internal/pkg/eql/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"

"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/hashicorp/go-multierror"

"github.com/elastic/elastic-agent/internal/pkg/eql/parser"
)
Expand Down Expand Up @@ -62,13 +63,45 @@ func New(expression string) (*Expression, error) {
return nil, ErrEmptyExpression
}

errorListener := newErrorListener()
input := antlr.NewInputStream(expression)
lexer := parser.NewEqlLexer(input)
lexer.RemoveErrorListeners()
lexer.AddErrorListener(errorListener)
tokens := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
p := parser.NewEqlParser(tokens)
p.RemoveErrorListeners()
p.AddErrorListener(errorListener)
tree := p.ExpList()

if errorListener.errors != nil {
return nil, errorListener.errors
}

return &Expression{expression: expression, tree: tree}, nil
}

// A simple listener that collects errors reported by antlr for reporting
// from eql.New. Create via newErrorListener.
type errorListener struct {
antlr.ErrorListener

// "errors" uses multierror to store all parse errors in a single
// error object.
errors error
}

func newErrorListener() *errorListener {
return &errorListener{antlr.NewDefaultErrorListener(), nil}
}

func (el *errorListener) SyntaxError(
recognizer antlr.Recognizer,
offendingSymbol interface{},
line, column int,
msg string,
e antlr.RecognitionException,
) {
el.errors = multierror.Append(el.errors,
fmt.Errorf("condition line %d column %d: %v", line, column, msg))
}

0 comments on commit a4234ac

Please sign in to comment.