diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 0000000..167e014 --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,11 @@ +quiet: False +keeptree: True +disable-version-string: True +with-expecter: True +mockname: "{{.InterfaceName}}" +filename: "{{.MockName}}.go" +outpkg: mocks +packages: + waffle/internal/rule: + interfaces: + Builder: \ No newline at end of file diff --git a/README.md b/README.md index 9fd5154..460b9b9 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ we are missing a real modular and open source **Web Application Firewall** that + golangci-lint + make (if windows, try using chocolatey) + openssl (if windows, try using git bash) -+ GRPC tools ++ [mockery](https://vektra.github.io/mockery/latest/installation/) 1. Create certificates and FS embed go file provider `make certs_windows` 2. Execute `docker compose up -d` to create needed infrastructure diff --git a/go.mod b/go.mod index 08f0997..dac4229 100644 --- a/go.mod +++ b/go.mod @@ -9,14 +9,19 @@ require ( github.com/goccy/go-yaml v1.11.3 github.com/google/uuid v1.6.0 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/stretchr/testify v1.9.0 nhooyr.io/websocket v1.8.10 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bc5d758..a4e7c1b 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d h1:wvStE9wLpws31NiW github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ= github.com/corazawaf/libinjection-go v0.1.3 h1:PUplAYho1BBl0tIVbhDsNRuVGIeUYSiCEc9oQpb2rJU= github.com/corazawaf/libinjection-go v0.1.3/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -27,6 +29,12 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -35,5 +43,9 @@ golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/internal/rule/compiler.go b/internal/rule/compiler.go index 2eac10f..3df5b71 100644 --- a/internal/rule/compiler.go +++ b/internal/rule/compiler.go @@ -25,17 +25,14 @@ type Compiler interface { } type CustomCompiler struct { + builder Builder } var _ Compiler = (*CustomCompiler)(nil) func (c *CustomCompiler) Compile(name, value string) (*Predicate, error) { - if len(name) == 0 { - return nil, ErrEmptyPredicateName - } - - if len(value) == 0 { - return nil, ErrEmptyPredicateValue + if err := validateInput(name, value); err != nil { + return nil, fmt.Errorf("validate name and value: %w", err) } variable, expression, err := getVariableAndLogicalExpression(strings.TrimSpace(value)) @@ -43,6 +40,24 @@ func (c *CustomCompiler) Compile(name, value string) (*Predicate, error) { return nil, fmt.Errorf("get variable and logical expression for predicate: %w", err) } + pred, err := c.builder.Build(name, variable, expression) + if err != nil { + return nil, fmt.Errorf("build predicate: %w", err) + } + + return pred, nil +} + +func validateInput(name string, value string) error { + if len(name) == 0 { + return ErrEmptyPredicateName + } + + if len(value) == 0 { + return ErrEmptyPredicateValue + } + + return nil } func getVariableAndLogicalExpression(value string) (string, string, error) { diff --git a/internal/rule/compiler_test.go b/internal/rule/compiler_test.go new file mode 100644 index 0000000..9efddb0 --- /dev/null +++ b/internal/rule/compiler_test.go @@ -0,0 +1,55 @@ +package rule + +import ( + "reflect" + "testing" +) + +func TestCustomCompiler_Compile(t *testing.T) { + type fields struct { + builder Builder + } + type args struct { + name string + value string + } + tests := []struct { + name string + fields fields + args args + want *Predicate + wantErr bool + }{ + { + name: "empty name, error returned", + args: args{ + name: "", + value: "p => p", + }, + wantErr: true, + }, + { + name: "empty value, error returned", + args: args{ + name: "name", + value: "", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &CustomCompiler{ + builder: tt.fields.builder, + } + got, err := c.Compile(tt.args.name, tt.args.value) + if (err != nil) != tt.wantErr { + t.Errorf("Compile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Compile() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/rule/predicate.go b/internal/rule/predicate.go index dbb997e..2aca1c4 100644 --- a/internal/rule/predicate.go +++ b/internal/rule/predicate.go @@ -1,4 +1,22 @@ package rule +type PredicateLogicalExpression func(payload []byte) (bool, error) + type Predicate struct { + Name string + LogicalExpression PredicateLogicalExpression +} + +type Builder interface { + Build(name, variable, expression string) (*Predicate, error) +} + +type PredicateBuilder struct { +} + +var _ Builder = (*PredicateBuilder)(nil) + +func (p *PredicateBuilder) Build(name, variable, expression string) (*Predicate, error) { + //TODO implement me + panic("implement me") } diff --git a/makefile b/makefile index 455c686..bae8220 100644 --- a/makefile +++ b/makefile @@ -23,3 +23,6 @@ certs: openssl req -new -sha256 -key ./cmd/proxy/.cert/server.key -out ./cmd/proxy/.cert/server.csr openssl x509 -req -in ./cmd/proxy/.cert/server.csr -CA ./cmd/proxy/.cert/ca.crt -CAkey ./cmd/proxy/.cert/ca.key -CAcreateserial -out ./cmd/proxy/.cert/server.crt -days 3650 -sha256 openssl x509 -in ./cmd/proxy/.cert/server.crt -text -noout + +mocks: + mockery \ No newline at end of file diff --git a/mocks/waffle/internal/rule/Builder.go b/mocks/waffle/internal/rule/Builder.go new file mode 100644 index 0000000..6b3f02c --- /dev/null +++ b/mocks/waffle/internal/rule/Builder.go @@ -0,0 +1,96 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + rule "waffle/internal/rule" + + mock "github.com/stretchr/testify/mock" +) + +// Builder is an autogenerated mock type for the Builder type +type Builder struct { + mock.Mock +} + +type Builder_Expecter struct { + mock *mock.Mock +} + +func (_m *Builder) EXPECT() *Builder_Expecter { + return &Builder_Expecter{mock: &_m.Mock} +} + +// Build provides a mock function with given fields: name, variable, expression +func (_m *Builder) Build(name string, variable string, expression string) (*rule.Predicate, error) { + ret := _m.Called(name, variable, expression) + + if len(ret) == 0 { + panic("no return value specified for Build") + } + + var r0 *rule.Predicate + var r1 error + if rf, ok := ret.Get(0).(func(string, string, string) (*rule.Predicate, error)); ok { + return rf(name, variable, expression) + } + if rf, ok := ret.Get(0).(func(string, string, string) *rule.Predicate); ok { + r0 = rf(name, variable, expression) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*rule.Predicate) + } + } + + if rf, ok := ret.Get(1).(func(string, string, string) error); ok { + r1 = rf(name, variable, expression) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Builder_Build_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Build' +type Builder_Build_Call struct { + *mock.Call +} + +// Build is a helper method to define mock.On call +// - name string +// - variable string +// - expression string +func (_e *Builder_Expecter) Build(name interface{}, variable interface{}, expression interface{}) *Builder_Build_Call { + return &Builder_Build_Call{Call: _e.mock.On("Build", name, variable, expression)} +} + +func (_c *Builder_Build_Call) Run(run func(name string, variable string, expression string)) *Builder_Build_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Builder_Build_Call) Return(_a0 *rule.Predicate, _a1 error) *Builder_Build_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Builder_Build_Call) RunAndReturn(run func(string, string, string) (*rule.Predicate, error)) *Builder_Build_Call { + _c.Call.Return(run) + return _c +} + +// NewBuilder creates a new instance of Builder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBuilder(t interface { + mock.TestingT + Cleanup(func()) +}) *Builder { + mock := &Builder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}