Validate rules in DNF

Ensure that rules passed to the evaluation engine are in DNF before
being evaluated against app install metadata.

Bug: 141971373
Test: atest FrameworksServicesTests:RuleEvaluatorTest
Change-Id: I42d1353ef13c1c9ccb6e6b4ecea5d58c8fc5dab5
This commit is contained in:
Khaled Abdelmohsen
2019-10-10 16:47:22 +01:00
parent 60029db843
commit e0d74cdf2e
2 changed files with 78 additions and 1 deletions

View File

@@ -37,6 +37,9 @@ final class RuleEvaluator {
/**
* Match the list of rules against an app install metadata.
*
* <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
* only. All rules are OR'ed together by default.
*
* @param rules The list of rules to evaluate.
* @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
* against.
@@ -45,7 +48,7 @@ final class RuleEvaluator {
*/
static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
for (Rule rule : rules) {
if (isMatch(rule, appInstallMetadata)) {
if (isConjunctionOfFormulas(rule.getFormula()) && isMatch(rule, appInstallMetadata)) {
return rule;
}
}
@@ -99,4 +102,25 @@ final class RuleEvaluator {
return false;
}
private static boolean isConjunctionOfFormulas(Formula formula) {
if (formula == null) {
return false;
}
if (isAtomicFormula(formula)) {
return true;
}
OpenFormula openFormula = (OpenFormula) formula;
return openFormula.getConnector() == OpenFormula.Connector.AND
&& openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
}
private static boolean isAtomicFormula(Formula formula) {
if (formula instanceof AtomicFormula) {
return true;
}
OpenFormula openFormula = (OpenFormula) formula;
return openFormula.getConnector() == OpenFormula.Connector.NOT
&& openFormula.getFormulas().get(0) instanceof AtomicFormula;
}
}

View File

@@ -127,4 +127,57 @@ public class RuleEvaluatorTest {
assertEquals(rule1, matchedRule);
}
@Test
public void testMatchRules_validForm() {
OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
PACKAGE_NAME_1),
new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
AtomicFormula.Operator.EQ,
APP_CERTIFICATE)));
Rule rule = new Rule(
openFormula, Rule.Effect.DENY);
Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
APP_INSTALL_METADATA);
assertEquals(rule, matchedRule);
}
@Test
public void testMatchRules_ruleNotInDNF() {
OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.OR, Arrays.asList(
new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
PACKAGE_NAME_1),
new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
AtomicFormula.Operator.EQ,
APP_CERTIFICATE)));
Rule rule = new Rule(
openFormula, Rule.Effect.DENY);
Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
APP_INSTALL_METADATA);
assertEquals(Rule.EMPTY, matchedRule);
}
@Test
public void testMatchRules_openFormulaWithNot() {
OpenFormula openSubFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
PACKAGE_NAME_2),
new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
AtomicFormula.Operator.EQ,
APP_CERTIFICATE)));
OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
Collections.singletonList(openSubFormula));
Rule rule = new Rule(
openFormula, Rule.Effect.DENY);
Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
APP_INSTALL_METADATA);
assertEquals(Rule.EMPTY, matchedRule);
}
}