diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java index ef5bb6ee1e798..23d0b40f08f68 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java @@ -41,15 +41,15 @@ public final class RuleXmlParser implements RuleParser { public static final String TAG = "RuleXmlParser"; // TODO: Use XML attributes - private static final String RULE_LIST_TAG = "RuleList"; - private static final String RULE_TAG = "Rule"; - private static final String OPEN_FORMULA_TAG = "OpenFormula"; - private static final String ATOMIC_FORMULA_TAG = "AtomicFormula"; - private static final String EFFECT_TAG = "Effect"; - private static final String KEY_TAG = "Key"; - private static final String OPERATOR_TAG = "Operator"; - private static final String VALUE_TAG = "Value"; - private static final String CONNECTOR_TAG = "Connector"; + private static final String RULE_LIST_TAG = "RL"; + private static final String RULE_TAG = "R"; + private static final String OPEN_FORMULA_TAG = "OF"; + private static final String ATOMIC_FORMULA_TAG = "AF"; + private static final String EFFECT_TAG = "E"; + private static final String KEY_TAG = "K"; + private static final String OPERATOR_TAG = "O"; + private static final String VALUE_TAG = "V"; + private static final String CONNECTOR_TAG = "C"; @Override public List parse(String ruleText) throws RuleParseException { @@ -80,16 +80,16 @@ public final class RuleXmlParser implements RuleParser { // Skipping the first event type, which is always {@link XmlPullParser.START_DOCUMENT} parser.next(); - // Processing the first tag; which should always be a tag. + // Processing the first tag; which should always be a RuleList tag. String nodeName = parser.getName(); - // Validating that the XML is starting with a tag. + // Validating that the XML is starting with a RuleList tag. // Note: This is the only breaking validation to run against XML files in the platform. // All rules inside are assumed to be validated at the server. If a rule is found to be // corrupt in the XML, it will be skipped to the next rule. if (!nodeName.equals(RULE_LIST_TAG)) { throw new RuntimeException( - String.format("Rules must start with tag. Found: %s at %s", nodeName, - parser.getPositionDescription())); + String.format("Rules must start with RuleList tag. Found: %s at %s", + nodeName, parser.getPositionDescription())); } int eventType; diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index ecb00a4b70254..ee95d2bb4d692 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -19,17 +19,18 @@ package com.android.server.integrity.serializer; import com.android.server.integrity.model.Rule; import java.io.OutputStream; +import java.util.List; /** A helper class to serialize rules from the {@link Rule} model to Xml representation. */ public class RuleBinarySerializer implements RuleSerializer { @Override - public void serialize(Rule rule, OutputStream outputStream) { + public void serialize(List rules, OutputStream outputStream) { // TODO: Implement stream serializer. } @Override - public String serialize(Rule rule) { + public String serialize(List rules) { // TODO: Implement text serializer. return null; } diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java new file mode 100644 index 0000000000000..60cfc4876414c --- /dev/null +++ b/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * 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.android.server.integrity.serializer; + +import android.annotation.NonNull; + +/** + * Thrown when rule serialization fails. + */ +public class RuleSerializeException extends Exception { + public RuleSerializeException(@NonNull String message) { + super(message); + } + + public RuleSerializeException(@NonNull String message, @NonNull Throwable cause) { + super(message, cause); + } +} diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java index 07a912f8aeab8..5c99c5a4c2dac 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java @@ -19,13 +19,14 @@ package com.android.server.integrity.serializer; import com.android.server.integrity.model.Rule; import java.io.OutputStream; +import java.util.List; /** A helper class to serialize rules from the {@link Rule} model. */ public interface RuleSerializer { - /** Serialize a rule to an output stream */ - void serialize(Rule rule, OutputStream outputStream); + /** Serialize rules to an output stream */ + void serialize(List rules, OutputStream outputStream) throws RuleSerializeException; - /** Serialize a rule to a string. */ - String serialize(Rule rule); + /** Serialize rules to a string. */ + String serialize(List rule) throws RuleSerializeException; } diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java index 62973e2b026f6..b0805fc488cbb 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java @@ -16,21 +16,142 @@ package com.android.server.integrity.serializer; +import android.util.Xml; + +import com.android.server.integrity.model.AtomicFormula; +import com.android.server.integrity.model.Formula; +import com.android.server.integrity.model.OpenFormula; import com.android.server.integrity.model.Rule; -import java.io.OutputStream; +import org.xmlpull.v1.XmlSerializer; -/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */ +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * A helper class to serialize rules from the {@link Rule} model to Xml representation. + */ public class RuleXmlSerializer implements RuleSerializer { + public static final String TAG = "RuleXmlSerializer"; + private static final String NAMESPACE = ""; + + private static final String RULE_LIST_TAG = "RL"; + private static final String RULE_TAG = "R"; + private static final String OPEN_FORMULA_TAG = "OF"; + private static final String ATOMIC_FORMULA_TAG = "AF"; + private static final String EFFECT_TAG = "E"; + private static final String KEY_TAG = "K"; + private static final String OPERATOR_TAG = "O"; + private static final String VALUE_TAG = "V"; + private static final String CONNECTOR_TAG = "C"; + @Override - public void serialize(Rule rule, OutputStream outputStream) { - // TODO: Implement stream serializer. + public void serialize(List rules, OutputStream outputStream) + throws RuleSerializeException { + try { + XmlSerializer xmlSerializer = Xml.newSerializer(); + xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); + serializeRules(rules, xmlSerializer); + } catch (Exception e) { + throw new RuleSerializeException(e.getMessage(), e); + } } @Override - public String serialize(Rule rule) { - // TODO: Implement text serializer. - return null; + public String serialize(List rules) throws RuleSerializeException { + try { + XmlSerializer xmlSerializer = Xml.newSerializer(); + StringWriter writer = new StringWriter(); + xmlSerializer.setOutput(writer); + serializeRules(rules, xmlSerializer); + return writer.toString(); + } catch (Exception e) { + throw new RuleSerializeException(e.getMessage(), e); + } + } + + private void serializeRules(List rules, XmlSerializer xmlSerializer) throws IOException { + xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG); + for (Rule rule : rules) { + serialize(rule, xmlSerializer); + } + xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG); + xmlSerializer.endDocument(); + } + + private void serialize(Rule rule, XmlSerializer xmlSerializer) throws IOException { + if (rule == null) { + return; + } + xmlSerializer.startTag(NAMESPACE, RULE_TAG); + serializeFormula(rule.getFormula(), xmlSerializer); + serializeValue(EFFECT_TAG, String.valueOf(rule.getEffect()), xmlSerializer); + xmlSerializer.endTag(NAMESPACE, RULE_TAG); + } + + private void serializeFormula(Formula formula, XmlSerializer xmlSerializer) throws IOException { + if (formula instanceof AtomicFormula) { + serializeAtomicFormula((AtomicFormula) formula, xmlSerializer); + } else if (formula instanceof OpenFormula) { + serializeOpenFormula((OpenFormula) formula, xmlSerializer); + } else { + throw new IllegalArgumentException( + String.format("Invalid formula type: %s", formula.getClass())); + } + } + + private void serializeOpenFormula(OpenFormula openFormula, XmlSerializer xmlSerializer) + throws IOException { + if (openFormula == null) { + return; + } + xmlSerializer.startTag(NAMESPACE, OPEN_FORMULA_TAG); + serializeValue(CONNECTOR_TAG, String.valueOf(openFormula.getConnector()), xmlSerializer); + for (Formula formula : openFormula.getFormulas()) { + serializeFormula(formula, xmlSerializer); + } + xmlSerializer.endTag(NAMESPACE, OPEN_FORMULA_TAG); + } + + private void serializeAtomicFormula(AtomicFormula atomicFormula, XmlSerializer xmlSerializer) + throws IOException { + if (atomicFormula == null) { + return; + } + xmlSerializer.startTag(NAMESPACE, ATOMIC_FORMULA_TAG); + serializeValue(KEY_TAG, String.valueOf(atomicFormula.getKey()), xmlSerializer); + if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) { + serializeValue(VALUE_TAG, + ((AtomicFormula.StringAtomicFormula) atomicFormula).getValue(), xmlSerializer); + } else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) { + serializeValue(OPERATOR_TAG, + String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getOperator()), + xmlSerializer); + serializeValue(VALUE_TAG, + String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getValue()), + xmlSerializer); + } else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) { + serializeValue(VALUE_TAG, + String.valueOf(((AtomicFormula.BooleanAtomicFormula) atomicFormula).getValue()), + xmlSerializer); + } else { + throw new IllegalArgumentException( + String.format("Invalid atomic formula type: %s", atomicFormula.getClass())); + } + xmlSerializer.endTag(NAMESPACE, ATOMIC_FORMULA_TAG); + } + + private void serializeValue(String tag, String value, XmlSerializer xmlSerializer) + throws IOException { + if (value == null) { + return; + } + xmlSerializer.startTag(NAMESPACE, tag); + xmlSerializer.text(value); + xmlSerializer.endTag(NAMESPACE, tag); } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java index 3b1c3c2b5e8b1..975689db5264f 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java @@ -39,19 +39,19 @@ public class RuleXmlParserTest { @Test public void testXmlStream_validOpenFormula() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); InputStream inputStream = new ByteArrayInputStream(ruleXmlOpenFormula.getBytes()); Rule expectedRule = new Rule(new OpenFormula(OpenFormula.NOT, Collections.singletonList( @@ -65,19 +65,19 @@ public class RuleXmlParserTest { @Test public void testXmlString_validOpenFormula_notConnector() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule(new OpenFormula(OpenFormula.NOT, Collections.singletonList( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"))), @@ -90,24 +90,24 @@ public class RuleXmlParserTest { @Test public void testXmlString_validOpenFormula_andConnector() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.AND + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + AtomicFormula.APP_CERTIFICATE + "" - + "" + AtomicFormula.EQ + "" - + "test_cert" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.AND + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + AtomicFormula.APP_CERTIFICATE + "" + + "" + AtomicFormula.EQ + "" + + "test_cert" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule(new OpenFormula(OpenFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), @@ -121,24 +121,24 @@ public class RuleXmlParserTest { @Test public void testXmlString_validOpenFormula_orConnector() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.OR + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + AtomicFormula.APP_CERTIFICATE + "" - + "" + AtomicFormula.EQ + "" - + "test_cert" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.OR + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + AtomicFormula.APP_CERTIFICATE + "" + + "" + AtomicFormula.EQ + "" + + "test_cert" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule(new OpenFormula(OpenFormula.OR, Arrays.asList( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), @@ -152,19 +152,19 @@ public class RuleXmlParserTest { @Test public void testXmlString_validOpenFormula_differentTagOrder() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule(new OpenFormula(OpenFormula.NOT, Collections.singletonList( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"))), @@ -177,24 +177,24 @@ public class RuleXmlParserTest { @Test public void testXmlString_invalidOpenFormula_invalidNumberOfFormulas() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + AtomicFormula.VERSION_CODE + "" - + "" + AtomicFormula.EQ + "" - + "1" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + AtomicFormula.VERSION_CODE + "" + + "" + AtomicFormula.EQ + "" + + "1" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -205,19 +205,19 @@ public class RuleXmlParserTest { @Test public void testXmlString_invalidOpenFormula_invalidOperator() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "INVALID_OPERATOR" - + "com.app.test" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "INVALID_OPERATOR" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -228,19 +228,19 @@ public class RuleXmlParserTest { @Test public void testXmlString_invalidOpenFormula_invalidEffect() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "INVALID_EFFECT" - + "" - + ""; + String ruleXmlOpenFormula = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "INVALID_EFFECT" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -251,19 +251,19 @@ public class RuleXmlParserTest { @Test public void testXmlString_invalidOpenFormula_invalidTags() throws Exception { - String ruleXmlOpenFormula = "" - + "" - + "" + String ruleXmlOpenFormula = "" + + "" + + "" + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -274,16 +274,16 @@ public class RuleXmlParserTest { @Test public void testXmlString_validAtomicFormula_stringValue() throws Exception { - String ruleXmlAtomicFormula = "" - + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), @@ -296,16 +296,16 @@ public class RuleXmlParserTest { @Test public void testXmlString_validAtomicFormula_integerValue() throws Exception { - String ruleXmlAtomicFormula = "" - + "" - + "" - + "" + AtomicFormula.VERSION_CODE + "" - + "" + AtomicFormula.EQ + "" - + "1" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.VERSION_CODE + "" + + "" + AtomicFormula.EQ + "" + + "1" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule( new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1), @@ -318,16 +318,16 @@ public class RuleXmlParserTest { @Test public void testXmlString_validAtomicFormula_booleanValue() throws Exception { - String ruleXmlAtomicFormula = "" - + "" - + "" - + "" + AtomicFormula.PRE_INSTALLED + "" - + "" + AtomicFormula.EQ + "" - + "true" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.PRE_INSTALLED + "" + + "" + AtomicFormula.EQ + "" + + "true" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule( new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), @@ -340,16 +340,16 @@ public class RuleXmlParserTest { @Test public void testXmlString_validAtomicFormula_differentTagOrder() throws Exception { - String ruleXmlAtomicFormula = "" - + "" - + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); Rule expectedRule = new Rule( new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), @@ -362,16 +362,16 @@ public class RuleXmlParserTest { @Test public void testXmlString_invalidAtomicFormula_invalidTags() throws Exception { - String ruleXmlAtomicFormula = "" - + "" - + "" + String ruleXmlAtomicFormula = "" + + "" + + "" + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -382,16 +382,16 @@ public class RuleXmlParserTest { @Test public void testXmlString_invalidAtomicFormula() throws Exception { - String ruleXmlAtomicFormula = "" - + "" - + "" - + "" + AtomicFormula.VERSION_CODE + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" + Rule.DENY + "" - + "" - + ""; + String ruleXmlAtomicFormula = "" + + "" + + "" + + "" + AtomicFormula.VERSION_CODE + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( @@ -402,44 +402,44 @@ public class RuleXmlParserTest { @Test public void testXmlString_withNoRuleList() { - String ruleXmlWithNoRuleList = "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + Rule.DENY + "" - + ""; + String ruleXmlWithNoRuleList = "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + ""; RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, - /* expectedExceptionMessageRegex */ "Rules must start with tag.", + /* expectedExceptionMessageRegex */ "Rules must start with RuleList tag", () -> xmlParser.parse(ruleXmlWithNoRuleList)); } @Test public void testXmlStream_withNoRuleList() { - String ruleXmlWithNoRuleList = "" - + "" - + "" + OpenFormula.NOT + "" - + "" - + "" + AtomicFormula.PACKAGE_NAME + "" - + "" + AtomicFormula.EQ + "" - + "com.app.test" - + "" - + "" - + "" + Rule.DENY + "" - + ""; + String ruleXmlWithNoRuleList = "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "" + AtomicFormula.EQ + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + ""; InputStream inputStream = new ByteArrayInputStream(ruleXmlWithNoRuleList.getBytes()); RuleParser xmlParser = new RuleXmlParser(); assertExpectException( RuleParseException.class, - /* expectedExceptionMessageRegex */ "Rules must start with tag.", + /* expectedExceptionMessageRegex */ "Rules must start with RuleList tag", () -> xmlParser.parse(inputStream)); } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java new file mode 100644 index 0000000000000..082fda82499a9 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * 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.android.server.integrity.serializer; + +import static com.android.server.testutils.TestUtils.assertExpectException; + +import static org.junit.Assert.assertEquals; + +import androidx.annotation.NonNull; + +import com.android.server.integrity.model.AppInstallMetadata; +import com.android.server.integrity.model.AtomicFormula; +import com.android.server.integrity.model.Formula; +import com.android.server.integrity.model.OpenFormula; +import com.android.server.integrity.model.Rule; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; + +@RunWith(JUnit4.class) +public class RuleXmlSerializerTest { + + @Test + public void testXmlString_serializeEmptyRule() throws Exception { + Rule rule = null; + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeMultipleRules_oneEmpty() throws Exception { + Rule rule1 = null; + Rule rule2 = new Rule( + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), + Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Arrays.asList(rule1, rule2)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlStream_serializeValidOpenFormula() throws Exception { + Rule rule = new Rule(new OpenFormula(OpenFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, + "com.app.test"))), Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + OutputStream outputStream = new ByteArrayOutputStream(); + String expectedRules = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + xmlSerializer.serialize(Collections.singletonList(rule), outputStream); + + String actualRules = outputStream.toString(); + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeValidOpenFormula_notConnector() throws Exception { + Rule rule = new Rule(new OpenFormula(OpenFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, + "com.app.test"))), Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + OpenFormula.NOT + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "com.app.test" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeValidOpenFormula_andConnector() throws Exception { + Rule rule = new Rule(new OpenFormula(OpenFormula.AND, + Arrays.asList(new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, + "com.app.test"), + new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, + "test_cert"))), Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + OpenFormula.AND + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "com.app.test" + + "" + + "" + + "" + AtomicFormula.APP_CERTIFICATE + "" + + "test_cert" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeValidOpenFormula_orConnector() throws Exception { + Rule rule = new Rule(new OpenFormula(OpenFormula.OR, + Arrays.asList(new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, + "com.app.test"), + new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, + "test_cert"))), Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + OpenFormula.OR + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "com.app.test" + + "" + + "" + + "" + AtomicFormula.APP_CERTIFICATE + "" + + "test_cert" + + "" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeValidAtomicFormula_stringValue() throws Exception { + Rule rule = new Rule( + new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test"), + Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + AtomicFormula.PACKAGE_NAME + "" + + "com.app.test" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeValidAtomicFormula_integerValue() throws Exception { + Rule rule = new Rule( + new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1), + Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + AtomicFormula.VERSION_CODE + "" + + "" + AtomicFormula.EQ + "" + + "1" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeValidAtomicFormula_booleanValue() throws Exception { + Rule rule = new Rule( + new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), + Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + String expectedRules = "" + + "" + + "" + + "" + AtomicFormula.PRE_INSTALLED + "" + + "true" + + "" + + "" + Rule.DENY + "" + + "" + + ""; + + String actualRules = xmlSerializer.serialize(Collections.singletonList(rule)); + + assertEquals(expectedRules, actualRules); + } + + @Test + public void testXmlString_serializeInvalidFormulaType() throws Exception { + Formula invalidFormula = getInvalidFormula(); + Rule rule = new Rule(invalidFormula, Rule.DENY); + RuleSerializer xmlSerializer = new RuleXmlSerializer(); + + assertExpectException( + RuleSerializeException.class, + /* expectedExceptionMessageRegex */ "Invalid formula type", + () -> xmlSerializer.serialize(Collections.singletonList(rule))); + } + + private Formula getInvalidFormula() { + return new Formula() { + @Override + public boolean isSatisfied(AppInstallMetadata appInstallMetadata) { + return false; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @NonNull + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public String toString() { + return super.toString(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + } + }; + } +}