Skip to content

fix(number validation): Don't track NaN, Infinity, -Infinity, or > 2^53 #249

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

Merged
merged 11 commits into from
Jan 25, 2019
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import javax.annotation.Nullable;

import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;

// Because json number is a double in most java json parsers. at this
// point we allow comparision of Integer and Double. The instance class is Double and
// Integer which would fail in our normal exact match. So, we are special casing for now. We have already filtered
Expand All @@ -34,7 +36,9 @@ protected ExactNumberMatch(Number value) {
@Nullable
public Boolean eval(Object attributeValue) {
try {
return value.doubleValue() == castToValueType(attributeValue, value).doubleValue();
if(isValidNumber(attributeValue)) {
return value.doubleValue() == castToValueType(attributeValue, value).doubleValue();
}
} catch (Exception e) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import javax.annotation.Nullable;

import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;

class GTMatch extends AttributeMatch<Number> {
Number value;

Expand All @@ -28,9 +30,12 @@ protected GTMatch(Number value) {
@Nullable
public Boolean eval(Object attributeValue) {
try {
return castToValueType(attributeValue, value).doubleValue() > value.doubleValue();
if(isValidNumber(attributeValue)) {
return castToValueType(attributeValue, value).doubleValue() > value.doubleValue();
}
} catch (Exception e) {
return null;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import javax.annotation.Nullable;

import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;

class LTMatch extends AttributeMatch<Number> {
Number value;

Expand All @@ -28,10 +30,13 @@ protected LTMatch(Number value) {
@Nullable
public Boolean eval(Object attributeValue) {
try {
return castToValueType(attributeValue, value).doubleValue() < value.doubleValue();
if(isValidNumber(attributeValue)) {
return castToValueType(attributeValue, value).doubleValue() < value.doubleValue();
}
} catch (Exception e) {
return null;
}
return null;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import javax.annotation.Nonnull;

import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;

public class MatchType {

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

private static boolean isValidNumber(Object conditionValue) {
if (conditionValue instanceof Integer) {
return Math.abs((Integer) conditionValue) <= 1e53;
} else if (conditionValue instanceof Double) {
Double value = ((Number) conditionValue).doubleValue();
return !(value.isNaN() || value.isInfinite());
}
return false;
}

private MatchType(String type, Match matcher) {
this.matchType = type;
this.matcher = matcher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import java.util.Map;
import java.util.UUID;

import static com.optimizely.ab.internal.AttributesUtil.isValidNumber;

public class EventFactory {
private static final Logger logger = LoggerFactory.getLogger(EventFactory.class);
static final String EVENT_ENDPOINT = "https://logx.optimizely.com/v1/events"; // Should be part of the datafile
Expand Down Expand Up @@ -163,9 +165,8 @@ private List<Attribute> buildAttributeList(ProjectConfig projectConfig, Map<Stri
// https://developers.optimizely.com/x/events/api/#Attribute
if (entry.getValue() == null ||
!((entry.getValue() instanceof String) ||
(entry.getValue() instanceof Integer) ||
(entry.getValue() instanceof Double) ||
(entry.getValue() instanceof Boolean))) {
(entry.getValue() instanceof Boolean) ||
(isValidNumber(entry.getValue())))) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
*
* Copyright 2019, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.optimizely.ab.internal;

public class AttributesUtil {

/**
* Validate that value is not infinite, NAN or greater than Math.pow(2, 53).
*
* @param value attribute value or condition value.
* @return boolean value of is valid or not.
*/
public static boolean isValidNumber(Object value) {
if (value instanceof Integer) {
return Math.abs((Integer) value) <= Math.pow(2, 53);
} else if (value instanceof Double || value instanceof Float) {
Double doubleValue = ((Number) value).doubleValue();
return !(doubleValue.isNaN() || doubleValue.isInfinite() || Math.abs(doubleValue) > Math.pow(2, 53));
} else if (value instanceof Long) {
return Math.abs((Long) value) <= Math.pow(2, 53);
}
return false;
}

}
Loading