Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

class DDEvaluator implements Evaluator, FeatureFlaggingGateway.ConfigListener {

Expand Down Expand Up @@ -148,6 +149,8 @@ public <T> ProviderEvaluation<T> evaluate(
.value(defaultValue)
.reason(Reason.DEFAULT.name())
.build();
} catch (final PatternSyntaxException e) {
return error(defaultValue, ErrorCode.PARSE_ERROR, e);
} catch (final NumberFormatException e) {
return error(defaultValue, ErrorCode.TYPE_MISMATCH, e);
} catch (final Exception e) {
Expand Down Expand Up @@ -251,12 +254,10 @@ private static boolean evaluateCondition(
}

private static boolean matchesRegex(final Object attributeValue, final Object conditionValue) {
try {
final Pattern pattern = Pattern.compile(String.valueOf(conditionValue));
return pattern.matcher(String.valueOf(attributeValue)).find();
} catch (Exception e) {
return false;
}
// PatternSyntaxException is intentionally not caught here so it propagates to evaluate(),
// which maps it to ErrorCode.PARSE_ERROR.
final Pattern pattern = Pattern.compile(String.valueOf(conditionValue));
return pattern.matcher(String.valueOf(attributeValue)).find();
}

private static boolean isOneOf(final Object attributeValue, final Object conditionValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,8 @@ private static List<TestCase<?>> evaluateTestCases() {
.flag("integer-flag")
.targetingKey("user-123")
.result(new Result<>(0.0).reason(ERROR.name()).errorCode(ErrorCode.TYPE_MISMATCH)),
// Variant value type mismatch: INTEGER flag with string variant value
// Variant stores "not-a-number" for an INTEGER flag; Double.parseDouble fails ->
// PARSE_ERROR
new TestCase<>(0)
.flag("integer-string-variant-flag")
.targetingKey("user-123")
Expand Down Expand Up @@ -489,7 +490,17 @@ private static List<TestCase<?>> evaluateTestCases() {
.result(
new Result<>("null-handled")
.reason(TARGETING_MATCH.name())
.variant("null-variant")));
.variant("null-variant")),
new TestCase<>("default")
.flag("invalid-regex-flag")
.targetingKey("user-123")
.context("email", "user@example.com")
.result(new Result<>("default").reason(ERROR.name()).errorCode(ErrorCode.PARSE_ERROR)),
new TestCase<>("default")
.flag("invalid-regex-not-matches-flag")
.targetingKey("user-123")
.context("email", "user@example.com")
.result(new Result<>("default").reason(ERROR.name()).errorCode(ErrorCode.PARSE_ERROR)));
}

@MethodSource("evaluateTestCases")
Expand Down Expand Up @@ -581,6 +592,8 @@ private ServerConfiguration createTestConfiguration() {
flags.put(
"integer-string-variant-flag",
createSimpleFlag("integer-string-variant-flag", ValueType.INTEGER, "not-a-number", "bad"));
flags.put("invalid-regex-flag", createInvalidRegexFlag());
flags.put("invalid-regex-not-matches-flag", createInvalidRegexNotMatchesFlag());
return new ServerConfiguration(null, null, null, flags);
}

Expand Down Expand Up @@ -1266,6 +1279,43 @@ private Flag createCountryRuleFlag() {
asList(usAllocation, globalAllocation));
}

private Flag createInvalidRegexFlag() {
final Map<String, Variant> variants = new HashMap<>();
variants.put("matched", new Variant("matched", "matched-value"));

// Condition with an intentionally invalid regex pattern (unclosed bracket)
final List<ConditionConfiguration> conditions =
singletonList(new ConditionConfiguration(ConditionOperator.MATCHES, "email", "[invalid"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

final List<Rule> rules = singletonList(new Rule(conditions));
final List<Split> splits = singletonList(new Split(emptyList(), "matched", null));
final Allocation allocation =
new Allocation("invalid-regex-alloc", rules, null, null, splits, false);

return new Flag(
"invalid-regex-flag", true, ValueType.STRING, variants, singletonList(allocation));
}

private Flag createInvalidRegexNotMatchesFlag() {
final Map<String, Variant> variants = new HashMap<>();
variants.put("excluded", new Variant("excluded", "excluded-value"));

// Condition with an intentionally invalid regex pattern (unclosed bracket) under NOT_MATCHES
final List<ConditionConfiguration> conditions =
singletonList(
new ConditionConfiguration(ConditionOperator.NOT_MATCHES, "email", "[invalid"));
final List<Rule> rules = singletonList(new Rule(conditions));
final List<Split> splits = singletonList(new Split(emptyList(), "excluded", null));
final Allocation allocation =
new Allocation("invalid-regex-not-matches-alloc", rules, null, null, splits, false);

return new Flag(
"invalid-regex-not-matches-flag",
true,
ValueType.STRING,
variants,
singletonList(allocation));
}

private static Map<String, Object> mapOf(final Object... props) {
final Map<String, Object> result = new HashMap<>(props.length << 1);
int index = 0;
Expand Down
Loading