Skip to content
Snippets Groups Projects
Commit bd237be4 authored by Max Loh's avatar Max Loh
Browse files

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
parent 85cae1da
No related branches found
No related tags found
No related merge requests found
Showing
with 130 additions and 24 deletions
......@@ -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;
......
......@@ -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.");
......
......@@ -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(
......
......@@ -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;
}
......@@ -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)));
}
......
......@@ -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) {
......
......@@ -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(
......
......@@ -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 {
......
......@@ -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);
}
......
/*
* 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);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment