1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "Logger.h" 18#include "ManifestValidator.h" 19#include "Maybe.h" 20#include "Source.h" 21#include "Util.h" 22 23#include <androidfw/ResourceTypes.h> 24 25namespace aapt { 26 27ManifestValidator::ManifestValidator(const android::ResTable& table) 28: mTable(table) { 29} 30 31bool ManifestValidator::validate(const Source& source, android::ResXMLParser* parser) { 32 SourceLogger logger(source); 33 34 android::ResXMLParser::event_code_t code; 35 while ((code = parser->next()) != android::ResXMLParser::END_DOCUMENT && 36 code != android::ResXMLParser::BAD_DOCUMENT) { 37 if (code != android::ResXMLParser::START_TAG) { 38 continue; 39 } 40 41 size_t len = 0; 42 const StringPiece16 namespaceUri(parser->getElementNamespace(&len), len); 43 if (!namespaceUri.empty()) { 44 continue; 45 } 46 47 const StringPiece16 name(parser->getElementName(&len), len); 48 if (name.empty()) { 49 logger.error(parser->getLineNumber()) 50 << "failed to get the element name." 51 << std::endl; 52 return false; 53 } 54 55 if (name == u"manifest") { 56 if (!validateManifest(source, parser)) { 57 return false; 58 } 59 } 60 } 61 return true; 62} 63 64Maybe<StringPiece16> ManifestValidator::getAttributeValue(android::ResXMLParser* parser, 65 size_t idx) { 66 android::Res_value value; 67 if (parser->getAttributeValue(idx, &value) < 0) { 68 return StringPiece16(); 69 } 70 71 const android::ResStringPool* pool = &parser->getStrings(); 72 if (value.dataType == android::Res_value::TYPE_REFERENCE) { 73 ssize_t strIdx = mTable.resolveReference(&value, 0x10000000u); 74 if (strIdx < 0) { 75 return {}; 76 } 77 pool = mTable.getTableStringBlock(strIdx); 78 } 79 80 if (value.dataType != android::Res_value::TYPE_STRING || !pool) { 81 return {}; 82 } 83 return util::getString(*pool, value.data); 84} 85 86Maybe<StringPiece16> ManifestValidator::getAttributeInlineValue(android::ResXMLParser* parser, 87 size_t idx) { 88 android::Res_value value; 89 if (parser->getAttributeValue(idx, &value) < 0) { 90 return StringPiece16(); 91 } 92 93 if (value.dataType != android::Res_value::TYPE_STRING) { 94 return {}; 95 } 96 return util::getString(parser->getStrings(), value.data); 97} 98 99bool ManifestValidator::validateInlineAttribute(android::ResXMLParser* parser, size_t idx, 100 SourceLogger& logger, 101 const StringPiece16& charSet) { 102 size_t len = 0; 103 StringPiece16 element(parser->getElementName(&len), len); 104 StringPiece16 attributeName(parser->getAttributeName(idx, &len), len); 105 Maybe<StringPiece16> result = getAttributeInlineValue(parser, idx); 106 if (!result) { 107 logger.error(parser->getLineNumber()) 108 << "<" 109 << element 110 << "> must have a '" 111 << attributeName 112 << "' attribute with a string literal value." 113 << std::endl; 114 return false; 115 } 116 return validateAttributeImpl(element, attributeName, result.value(), charSet, 117 parser->getLineNumber(), logger); 118} 119 120bool ManifestValidator::validateAttribute(android::ResXMLParser* parser, size_t idx, 121 SourceLogger& logger, const StringPiece16& charSet) { 122 size_t len = 0; 123 StringPiece16 element(parser->getElementName(&len), len); 124 StringPiece16 attributeName(parser->getAttributeName(idx, &len), len); 125 Maybe<StringPiece16> result = getAttributeValue(parser, idx); 126 if (!result) { 127 logger.error(parser->getLineNumber()) 128 << "<" 129 << element 130 << "> must have a '" 131 << attributeName 132 << "' attribute that points to a string." 133 << std::endl; 134 return false; 135 } 136 return validateAttributeImpl(element, attributeName, result.value(), charSet, 137 parser->getLineNumber(), logger); 138} 139 140bool ManifestValidator::validateAttributeImpl(const StringPiece16& element, 141 const StringPiece16& attributeName, 142 const StringPiece16& attributeValue, 143 const StringPiece16& charSet, size_t lineNumber, 144 SourceLogger& logger) { 145 StringPiece16::const_iterator badIter = 146 util::findNonAlphaNumericAndNotInSet(attributeValue, charSet); 147 if (badIter != attributeValue.end()) { 148 logger.error(lineNumber) 149 << "tag <" 150 << element 151 << "> attribute '" 152 << attributeName 153 << "' has invalid character '" 154 << StringPiece16(badIter, 1) 155 << "'." 156 << std::endl; 157 return false; 158 } 159 160 if (!attributeValue.empty()) { 161 StringPiece16 trimmed = util::trimWhitespace(attributeValue); 162 if (attributeValue.begin() != trimmed.begin()) { 163 logger.error(lineNumber) 164 << "tag <" 165 << element 166 << "> attribute '" 167 << attributeName 168 << "' can not start with whitespace." 169 << std::endl; 170 return false; 171 } 172 173 if (attributeValue.end() != trimmed.end()) { 174 logger.error(lineNumber) 175 << "tag <" 176 << element 177 << "> attribute '" 178 << attributeName 179 << "' can not end with whitespace." 180 << std::endl; 181 return false; 182 } 183 } 184 return true; 185} 186 187constexpr const char16_t* kPackageIdentSet = u"._"; 188 189bool ManifestValidator::validateManifest(const Source& source, android::ResXMLParser* parser) { 190 bool error = false; 191 SourceLogger logger(source); 192 193 const StringPiece16 kAndroid = u"android"; 194 const StringPiece16 kPackage = u"package"; 195 const StringPiece16 kSharedUserId = u"sharedUserId"; 196 197 ssize_t idx; 198 199 idx = parser->indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()); 200 if (idx < 0) { 201 logger.error(parser->getLineNumber()) 202 << "missing package attribute." 203 << std::endl; 204 error = true; 205 } else { 206 error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet); 207 } 208 209 idx = parser->indexOfAttribute(kAndroid.data(), kAndroid.size(), 210 kSharedUserId.data(), kSharedUserId.size()); 211 if (idx >= 0) { 212 error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet); 213 } 214 return !error; 215} 216 217} // namespace aapt 218