Skip to content

Commit 839b7d3

Browse files
author
Dave Johnston
committed
(MAINT) If an operator is nil, it can cause a panic
What: Adding check to ensure we only use operator if it is non-nul. Why: Its possible for a clause to have a nil operator, but the only valid scenario for this, is if it is a segmentMatch operation. We need protective code to ensure we don't accidently call a nil operator.
1 parent 40a67c4 commit 839b7d3

File tree

3 files changed

+66
-36
lines changed

3 files changed

+66
-36
lines changed

evaluation/feature.go

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"encoding/json"
55
"fmt"
66

7+
"github.com/labstack/gommon/log"
8+
79
"github.com/drone/ff-golang-server-sdk/types"
810

911
"reflect"
@@ -43,26 +45,34 @@ type Clause struct {
4345

4446
// Evaluate clause using target but it can be used also with segments if Op field is segmentMach
4547
func (c *Clause) Evaluate(target *Target, segments Segments, operator types.ValueType) bool {
46-
switch c.Op {
47-
case segmentMatchOperator:
48+
49+
// Special case - segment matcher doesn't require a
50+
// valid operator.
51+
if c.Op == segmentMatchOperator {
4852
if segments == nil {
4953
return false
5054
}
5155
return segments.Evaluate(target)
52-
case inOperator:
53-
return operator.In(c.Value)
54-
case equalOperator:
55-
return operator.Equal(c.Value)
56-
case gtOperator:
57-
return operator.GreaterThan(c.Value)
58-
case startsWithOperator:
59-
return operator.StartsWith(c.Value)
60-
case endsWithOperator:
61-
return operator.EndsWith(c.Value)
62-
case containsOperator:
63-
return operator.Contains(c.Value)
64-
case equalSensitiveOperator:
65-
return operator.EqualSensitive(c.Value)
56+
}
57+
58+
// Ensure operator is valid and not nil
59+
if operator != nil {
60+
switch c.Op {
61+
case inOperator:
62+
return operator.In(c.Value)
63+
case equalOperator:
64+
return operator.Equal(c.Value)
65+
case gtOperator:
66+
return operator.GreaterThan(c.Value)
67+
case startsWithOperator:
68+
return operator.StartsWith(c.Value)
69+
case endsWithOperator:
70+
return operator.EndsWith(c.Value)
71+
case containsOperator:
72+
return operator.Contains(c.Value)
73+
case equalSensitiveOperator:
74+
return operator.EqualSensitive(c.Value)
75+
}
6676
}
6777
return false
6878
}
@@ -71,11 +81,17 @@ func (c *Clause) Evaluate(target *Target, segments Segments, operator types.Valu
7181
type Clauses []Clause
7282

7383
// Evaluate clauses using target but it can be used also with segments if Op field is segmentMach
84+
// TODO this func can return false because of an error. We need a way to indicate to the caller if this is false
85+
// because it evaluated false, or because it actually failed to work.
7486
func (c Clauses) Evaluate(target *Target, segments Segments) bool {
7587
// AND operation
7688
for _, clause := range c {
7789
// operator should be evaluated based on type of attribute
78-
op := target.GetOperator(clause.Attribute)
90+
op, err := target.GetOperator(clause.Attribute)
91+
if err != nil {
92+
log.Warn(err)
93+
//return false
94+
}
7995
if !clause.Evaluate(target, segments, op) {
8096
return false
8197
}

evaluation/target.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package evaluation
22

33
import (
4+
"fmt"
5+
46
"github.com/drone/ff-golang-server-sdk/types"
57

68
"reflect"
@@ -28,22 +30,22 @@ func (t Target) GetAttrValue(attr string) reflect.Value {
2830
}
2931

3032
// GetOperator returns interface based on attribute value
31-
func (t Target) GetOperator(attr string) types.ValueType {
33+
func (t Target) GetOperator(attr string) (types.ValueType, error) {
3234
value := t.GetAttrValue(attr)
3335
switch value.Kind() {
3436
case reflect.Bool:
35-
return types.Boolean(value.Bool())
37+
return types.Boolean(value.Bool()), nil
3638
case reflect.String:
37-
return types.String(value.String())
39+
return types.String(value.String()), nil
3840
case reflect.Float64, reflect.Float32:
39-
return types.Number(value.Float())
41+
return types.Number(value.Float()), nil
4042
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Uint, reflect.Uint16,
4143
reflect.Uint32, reflect.Uint64, reflect.Uint8:
42-
return types.Integer(value.Int())
44+
return types.Integer(value.Int()), nil
4345
case reflect.Array, reflect.Chan, reflect.Complex128, reflect.Complex64, reflect.Func, reflect.Interface,
4446
reflect.Invalid, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Uintptr, reflect.UnsafePointer:
45-
return nil
47+
fallthrough
4648
default:
47-
return nil
49+
return nil, fmt.Errorf("unexpected type: %s", value.Kind().String())
4850
}
4951
}

evaluation/target_test.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ func TestTarget_GetOperator(t1 *testing.T) {
2121
attr string
2222
}
2323
tests := []struct {
24-
name string
25-
fields fields
26-
args args
27-
want types.ValueType
24+
name string
25+
fields fields
26+
args args
27+
want types.ValueType
28+
wantErr bool
2829
}{
2930
{name: "boolean operator", fields: struct {
3031
Identifier string
@@ -68,8 +69,13 @@ func TestTarget_GetOperator(t1 *testing.T) {
6869
Anonymous: &val.fields.Anonymous,
6970
Attributes: &val.fields.Attributes,
7071
}
71-
if got := t.GetOperator(val.args.attr); !reflect.DeepEqual(got, val.want) {
72-
t1.Errorf("GetOperator() = %v, want %v", got, val.want)
72+
got, err := t.GetOperator(val.args.attr)
73+
if (err != nil) != val.wantErr {
74+
t1.Errorf("GetOperator() error = %v, wantErr %v", err, val.wantErr)
75+
return
76+
}
77+
if !reflect.DeepEqual(got, val.want) {
78+
t1.Errorf("GetOperator() error = %v, want %v", err, val.want)
7379
}
7480
})
7581
}
@@ -142,10 +148,11 @@ func TestTarget_GetOperator1(t1 *testing.T) {
142148

143149
name := "John"
144150
tests := []struct {
145-
name string
146-
fields fields
147-
args args
148-
want types.ValueType
151+
name string
152+
fields fields
153+
args args
154+
want types.ValueType
155+
wantErr bool
149156
}{
150157
{name: "bool operator", fields: struct {
151158
Identifier string
@@ -189,8 +196,13 @@ func TestTarget_GetOperator1(t1 *testing.T) {
189196
Anonymous: &val.fields.Anonymous,
190197
Attributes: &val.fields.Attributes,
191198
}
192-
if got := t.GetOperator(val.args.attr); !reflect.DeepEqual(got, val.want) {
193-
t1.Errorf("GetOperator() = %v, want %v", got, val.want)
199+
got, err := t.GetOperator(val.args.attr)
200+
if (err != nil) != val.wantErr {
201+
t1.Errorf("GetOperator() error = %v, wantErr %v", err, val.wantErr)
202+
return
203+
}
204+
if !reflect.DeepEqual(got, val.want) {
205+
t1.Errorf("GetOperator() error = %v, want %v", err, val.want)
194206
}
195207
})
196208
}

0 commit comments

Comments
 (0)