From bd237be4563c5ae24cdcb318bea747bb853a07aa Mon Sep 17 00:00:00 2001 From: Max Loh <mloh@google.com> Date: Mon, 18 Mar 2024 15:31:04 -0700 Subject: [PATCH] ASL validation logic Adds validation logic for isSharingOptional, isCollectionOptional, and various expected elements in the input XML file. Bug: 287487923 Test: TODO in future CLs Change-Id: I0a2261ec3c71a1d2df977810d065dfc5a4dda5e3 --- .../aslgen/java/com/android/aslgen/Main.java | 7 ++- .../android/asllib/AndroidSafetyLabel.java | 10 ++-- .../asllib/AndroidSafetyLabelFactory.java | 5 +- .../asllib/AslMarshallableFactory.java | 4 +- .../android/asllib/DataCategoryFactory.java | 8 ++- .../com/android/asllib/DataLabelsFactory.java | 55 +++++++++++++++++-- .../com/android/asllib/DataTypeFactory.java | 4 +- .../android/asllib/SafetyLabelsFactory.java | 4 +- .../lib/java/com/android/asllib/XmlUtils.java | 24 +++++--- .../asllib/util/MalformedXmlException.java | 33 +++++++++++ 10 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java diff --git a/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java b/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java index df003b6aeab2..fb7a6ab42d95 100644 --- a/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java +++ b/tools/app_metadata_bundles/src/aslgen/java/com/android/aslgen/Main.java @@ -18,6 +18,7 @@ package com.android.aslgen; import com.android.asllib.AndroidSafetyLabel; import com.android.asllib.AndroidSafetyLabel.Format; +import com.android.asllib.util.MalformedXmlException; import org.xml.sax.SAXException; @@ -32,7 +33,11 @@ public class Main { /** Takes the options to make file conversion. */ public static void main(String[] args) - throws IOException, ParserConfigurationException, SAXException, TransformerException { + throws IOException, + ParserConfigurationException, + SAXException, + TransformerException, + MalformedXmlException { String inFile = null; String outFile = null; diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java index 0f7ce6894063..bc8063ef7b5f 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabel.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; @@ -53,7 +55,7 @@ public class AndroidSafetyLabel implements AslMarshallable { /** Reads a {@link AndroidSafetyLabel} from an {@link InputStream}. */ // TODO(b/329902686): Support parsing from on-device. public static AndroidSafetyLabel readFromStream(InputStream in, Format format) - throws IOException, ParserConfigurationException, SAXException { + throws IOException, ParserConfigurationException, SAXException, MalformedXmlException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); Document document = factory.newDocumentBuilder().parse(in); @@ -65,9 +67,9 @@ public class AndroidSafetyLabel implements AslMarshallable { return new AndroidSafetyLabelFactory() .createFromHrElements( - XmlUtils.asElementList( - document.getElementsByTagName( - XmlUtils.HR_TAG_APP_METADATA_BUNDLES))); + List.of( + XmlUtils.getSingleElement( + document, XmlUtils.HR_TAG_APP_METADATA_BUNDLES))); case ON_DEVICE: throw new IllegalArgumentException( "Parsing from on-device format is not supported at this time."); diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java index 9b0f05b0c633..7e7fcf9c08ba 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AndroidSafetyLabelFactory.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Element; import java.util.List; @@ -24,7 +26,8 @@ public class AndroidSafetyLabelFactory implements AslMarshallableFactory<Android /** Creates an {@link AndroidSafetyLabel} from human-readable DOM element */ @Override - public AndroidSafetyLabel createFromHrElements(List<Element> appMetadataBundles) { + public AndroidSafetyLabel createFromHrElements(List<Element> appMetadataBundles) + throws MalformedXmlException { Element appMetadataBundlesEle = XmlUtils.getSingleElement(appMetadataBundles); Element safetyLabelsEle = XmlUtils.getSingleChildElement( diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java index b607353791ff..b8f9f0ef6235 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/AslMarshallableFactory.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Element; import java.util.List; @@ -23,5 +25,5 @@ import java.util.List; public interface AslMarshallableFactory<T extends AslMarshallable> { /** Creates an {@link AslMarshallableFactory} from human-readable DOM element */ - T createFromHrElements(List<Element> elements); + T createFromHrElements(List<Element> elements) throws MalformedXmlException; } diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java index 5a52591eaf8c..d9463452d7bc 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataCategoryFactory.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Element; import java.util.HashMap; @@ -24,12 +26,16 @@ import java.util.Map; public class DataCategoryFactory implements AslMarshallableFactory<DataCategory> { @Override - public DataCategory createFromHrElements(List<Element> elements) { + public DataCategory createFromHrElements(List<Element> elements) throws MalformedXmlException { String categoryName = null; Map<String, DataType> dataTypeMap = new HashMap<String, DataType>(); for (Element ele : elements) { categoryName = ele.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY); String dataTypeName = ele.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE); + if (!DataTypeConstants.getValidDataTypes().contains(dataTypeName)) { + throw new MalformedXmlException( + String.format("Unrecognized data type name: %s", dataTypeName)); + } dataTypeMap.put(dataTypeName, new DataTypeFactory().createFromHrElements(List.of(ele))); } diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java index c758ab923bbf..1adb140f446d 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataLabelsFactory.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -29,7 +31,7 @@ public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> { /** Creates a {@link DataLabels} from the human-readable DOM element. */ @Override - public DataLabels createFromHrElements(List<Element> elements) { + public DataLabels createFromHrElements(List<Element> elements) throws MalformedXmlException { Element ele = XmlUtils.getSingleElement(elements); Map<String, DataCategory> dataAccessed = getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_ACCESSED); @@ -37,13 +39,54 @@ public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> { getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED); Map<String, DataCategory> dataShared = getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED); + + // Validate booleans such as isCollectionOptional, isSharingOptional. + for (DataCategory dataCategory : dataAccessed.values()) { + for (DataType dataType : dataCategory.getDataTypes().values()) { + if (dataType.getIsSharingOptional() != null) { + throw new MalformedXmlException( + String.format( + "isSharingOptional was unexpectedly defined on a DataType" + + " belonging to data accessed: %s", + dataType.getDataTypeName())); + } + if (dataType.getIsCollectionOptional() != null) { + throw new MalformedXmlException( + String.format( + "isCollectionOptional was unexpectedly defined on a DataType" + + " belonging to data accessed: %s", + dataType.getDataTypeName())); + } + } + } + for (DataCategory dataCategory : dataCollected.values()) { + for (DataType dataType : dataCategory.getDataTypes().values()) { + if (dataType.getIsSharingOptional() != null) { + throw new MalformedXmlException( + String.format( + "isSharingOptional was unexpectedly defined on a DataType" + + " belonging to data collected: %s", + dataType.getDataTypeName())); + } + } + } + for (DataCategory dataCategory : dataShared.values()) { + for (DataType dataType : dataCategory.getDataTypes().values()) { + if (dataType.getIsCollectionOptional() != null) { + throw new MalformedXmlException( + String.format( + "isCollectionOptional was unexpectedly defined on a DataType" + + " belonging to data shared: %s", + dataType.getDataTypeName())); + } + } + } + return new DataLabels(dataAccessed, dataCollected, dataShared); } private static Map<String, DataCategory> getDataCategoriesWithTag( - Element dataLabelsEle, String dataCategoryUsageTypeTag) { - Map<String, Map<String, DataType>> dataTypeMap = - new HashMap<String, Map<String, DataType>>(); + Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException { NodeList dataUsedNodeList = dataLabelsEle.getElementsByTagName(dataCategoryUsageTypeTag); Map<String, DataCategory> dataCategoryMap = new HashMap<String, DataCategory>(); @@ -51,6 +94,10 @@ public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> { for (int i = 0; i < dataUsedNodeList.getLength(); i++) { Element dataUsedEle = (Element) dataUsedNodeList.item(i); String dataCategoryName = dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY); + if (!DataCategoryConstants.getValidDataCategories().contains(dataCategoryName)) { + throw new MalformedXmlException( + String.format("Unrecognized category name: %s", dataCategoryName)); + } dataCategoryNames.add(dataCategoryName); } for (String dataCategoryName : dataCategoryNames) { diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java index 99f8a8b1b152..e3d1587d860c 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/DataTypeFactory.java @@ -35,10 +35,10 @@ public class DataTypeFactory implements AslMarshallableFactory<DataType> { .collect(Collectors.toUnmodifiableSet()); Boolean isCollectionOptional = XmlUtils.fromString( - hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL)); + hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL)); Boolean isSharingOptional = XmlUtils.fromString( - hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL)); + hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL)); Boolean ephemeral = XmlUtils.fromString(hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_EPHEMERAL)); return new DataType( diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java index 68e83fe30db1..80b9f5783b9d 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/SafetyLabelsFactory.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Element; import java.util.List; @@ -24,7 +26,7 @@ public class SafetyLabelsFactory implements AslMarshallableFactory<SafetyLabels> /** Creates a {@link SafetyLabels} from the human-readable DOM element. */ @Override - public SafetyLabels createFromHrElements(List<Element> elements) { + public SafetyLabels createFromHrElements(List<Element> elements) throws MalformedXmlException { Element safetyLabelsEle = XmlUtils.getSingleElement(elements); Long version; try { diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java index 3c89a308036f..3bc9ccc2138b 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/XmlUtils.java @@ -16,6 +16,8 @@ package com.android.asllib; +import com.android.asllib.util.MalformedXmlException; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -61,30 +63,34 @@ public class XmlUtils { public static final String FALSE_STR = "false"; /** Gets the single top-level {@link Element} having the {@param tagName}. */ - public static Element getSingleElement(Document doc, String tagName) { + public static Element getSingleElement(Document doc, String tagName) + throws MalformedXmlException { var elements = doc.getElementsByTagName(tagName); - return getSingleElement(elements); + return getSingleElement(elements, tagName); } /** * Gets the single {@link Element} within {@param parentEle} and having the {@param tagName}. */ - public static Element getSingleChildElement(Element parentEle, String tagName) { + public static Element getSingleChildElement(Element parentEle, String tagName) + throws MalformedXmlException { var elements = parentEle.getElementsByTagName(tagName); - return getSingleElement(elements); + return getSingleElement(elements, tagName); } /** Gets the single {@link Element} from {@param elements} */ - public static Element getSingleElement(NodeList elements) { + public static Element getSingleElement(NodeList elements, String tagName) + throws MalformedXmlException { if (elements.getLength() != 1) { - throw new IllegalArgumentException( + throw new MalformedXmlException( String.format( - "Expected 1 element in NodeList but got %s.", elements.getLength())); + "Expected 1 element \"%s\" in NodeList but got %s.", + tagName, elements.getLength())); } var elementAsNode = elements.item(0); if (!(elementAsNode instanceof Element)) { - throw new IllegalStateException( - String.format("%s was not an element.", elementAsNode.getNodeName())); + throw new MalformedXmlException( + String.format("%s was not a valid XML element.", tagName)); } return ((Element) elementAsNode); } diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java new file mode 100644 index 000000000000..216df56c453e --- /dev/null +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/MalformedXmlException.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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.asllib.util; + +public class MalformedXmlException extends Exception { + /** Constructs an {@code MalformedXmlException} with no detail message. */ + public MalformedXmlException() { + super(); + } + + /** + * Constructs an {@code MalformedXmlException} with the specified detail message. + * + * @param s the detail message. + */ + public MalformedXmlException(String s) { + super(s); + } +} -- GitLab