Skip to content

Commit ea63898

Browse files
mnoman09aliabbasrizvi
authored andcommitted
fix(number validation): Don't track NaN, Infinity, -Infinity, or > 2^53 (#249)
1 parent 28097f0 commit ea63898

File tree

8 files changed

+376
-65
lines changed

8 files changed

+376
-65
lines changed

core-api/src/main/java/com/optimizely/ab/config/audience/match/ExactNumberMatch.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import javax.annotation.Nullable;
2222

23+
import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;
24+
2325
// Because json number is a double in most java json parsers. at this
2426
// point we allow comparision of Integer and Double. The instance class is Double and
2527
// Integer which would fail in our normal exact match. So, we are special casing for now. We have already filtered
@@ -34,7 +36,9 @@ protected ExactNumberMatch(Number value) {
3436
@Nullable
3537
public Boolean eval(Object attributeValue) {
3638
try {
37-
return value.doubleValue() == castToValueType(attributeValue, value).doubleValue();
39+
if(isValidNumber(attributeValue)) {
40+
return value.doubleValue() == castToValueType(attributeValue, value).doubleValue();
41+
}
3842
} catch (Exception e) {
3943
}
4044

core-api/src/main/java/com/optimizely/ab/config/audience/match/GTMatch.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import javax.annotation.Nullable;
2020

21+
import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;
22+
2123
class GTMatch extends AttributeMatch<Number> {
2224
Number value;
2325

@@ -28,9 +30,12 @@ protected GTMatch(Number value) {
2830
@Nullable
2931
public Boolean eval(Object attributeValue) {
3032
try {
31-
return castToValueType(attributeValue, value).doubleValue() > value.doubleValue();
33+
if(isValidNumber(attributeValue)) {
34+
return castToValueType(attributeValue, value).doubleValue() > value.doubleValue();
35+
}
3236
} catch (Exception e) {
3337
return null;
3438
}
39+
return null;
3540
}
3641
}

core-api/src/main/java/com/optimizely/ab/config/audience/match/LTMatch.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import javax.annotation.Nullable;
2020

21+
import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;
22+
2123
class LTMatch extends AttributeMatch<Number> {
2224
Number value;
2325

@@ -28,10 +30,13 @@ protected LTMatch(Number value) {
2830
@Nullable
2931
public Boolean eval(Object attributeValue) {
3032
try {
31-
return castToValueType(attributeValue, value).doubleValue() < value.doubleValue();
33+
if(isValidNumber(attributeValue)) {
34+
return castToValueType(attributeValue, value).doubleValue() < value.doubleValue();
35+
}
3236
} catch (Exception e) {
3337
return null;
3438
}
39+
return null;
3540
}
3641
}
3742

core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchType.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import javax.annotation.Nonnull;
2323

24+
import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;
25+
2426
public class MatchType {
2527

2628
public static final Logger logger = LoggerFactory.getLogger(MatchType.class);
@@ -70,16 +72,6 @@ public static MatchType getMatchType(String matchType, Object conditionValue) th
7072
throw new UnexpectedValueTypeException();
7173
}
7274

73-
private static boolean isValidNumber(Object conditionValue) {
74-
if (conditionValue instanceof Integer) {
75-
return Math.abs((Integer) conditionValue) <= 1e53;
76-
} else if (conditionValue instanceof Double) {
77-
Double value = ((Number) conditionValue).doubleValue();
78-
return !(value.isNaN() || value.isInfinite());
79-
}
80-
return false;
81-
}
82-
8375
private MatchType(String type, Match matcher) {
8476
this.matchType = type;
8577
this.matcher = matcher;

core-api/src/main/java/com/optimizely/ab/event/internal/EventFactory.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import java.util.Map;
4040
import java.util.UUID;
4141

42+
import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;
43+
4244
public class EventFactory {
4345
private static final Logger logger = LoggerFactory.getLogger(EventFactory.class);
4446
static final String EVENT_ENDPOINT = "https://logx.optimizely.com/v1/events"; // Should be part of the datafile
@@ -163,9 +165,8 @@ private List<Attribute> buildAttributeList(ProjectConfig projectConfig, Map<Stri
163165
// https://developers.optimizely.com/x/events/api/#Attribute
164166
if (entry.getValue() == null ||
165167
!((entry.getValue() instanceof String) ||
166-
(entry.getValue() instanceof Integer) ||
167-
(entry.getValue() instanceof Double) ||
168-
(entry.getValue() instanceof Boolean))) {
168+
(entry.getValue() instanceof Boolean) ||
169+
(isValidNumber(entry.getValue())))) {
169170
continue;
170171
}
171172

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
*
3+
* Copyright 2019, Optimizely and contributors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.optimizely.ab.internal;
19+
20+
public class AttributesUtil {
21+
22+
/**
23+
* Validate that value is not infinite, NAN or greater than Math.pow(2, 53).
24+
*
25+
* @param value attribute value or condition value.
26+
* @return boolean value of is valid or not.
27+
*/
28+
public static boolean isValidNumber(Object value) {
29+
if (value instanceof Integer) {
30+
return Math.abs((Integer) value) <= Math.pow(2, 53);
31+
} else if (value instanceof Double || value instanceof Float) {
32+
Double doubleValue = ((Number) value).doubleValue();
33+
return !(doubleValue.isNaN() || doubleValue.isInfinite() || Math.abs(doubleValue) > Math.pow(2, 53));
34+
} else if (value instanceof Long) {
35+
return Math.abs((Long) value) <= Math.pow(2, 53);
36+
}
37+
return false;
38+
}
39+
40+
}

0 commit comments

Comments
 (0)