diff options
Diffstat (limited to 'tools/aapt2/ManifestValidator.cpp')
| -rw-r--r-- | tools/aapt2/ManifestValidator.cpp | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/tools/aapt2/ManifestValidator.cpp b/tools/aapt2/ManifestValidator.cpp new file mode 100644 index 0000000..7ec0bc7 --- /dev/null +++ b/tools/aapt2/ManifestValidator.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "Logger.h" +#include "ManifestValidator.h" +#include "Maybe.h" +#include "Source.h" +#include "Util.h" + +#include <androidfw/ResourceTypes.h> + +namespace aapt { + +ManifestValidator::ManifestValidator(const android::ResTable& table) +: mTable(table) { +} + +bool ManifestValidator::validate(const Source& source, android::ResXMLParser* parser) { + SourceLogger logger(source); + + android::ResXMLParser::event_code_t code; + while ((code = parser->next()) != android::ResXMLParser::END_DOCUMENT && + code != android::ResXMLParser::BAD_DOCUMENT) { + if (code != android::ResXMLParser::START_TAG) { + continue; + } + + size_t len = 0; + const StringPiece16 namespaceUri(parser->getElementNamespace(&len), len); + if (!namespaceUri.empty()) { + continue; + } + + const StringPiece16 name(parser->getElementName(&len), len); + if (name.empty()) { + logger.error(parser->getLineNumber()) + << "failed to get the element name." + << std::endl; + return false; + } + + if (name == u"manifest") { + if (!validateManifest(source, parser)) { + return false; + } + } + } + return true; +} + +Maybe<StringPiece16> ManifestValidator::getAttributeValue(android::ResXMLParser* parser, + size_t idx) { + android::Res_value value; + if (parser->getAttributeValue(idx, &value) < 0) { + return StringPiece16(); + } + + const android::ResStringPool* pool = &parser->getStrings(); + if (value.dataType == android::Res_value::TYPE_REFERENCE) { + ssize_t strIdx = mTable.resolveReference(&value, 0x10000000u); + if (strIdx < 0) { + return {}; + } + pool = mTable.getTableStringBlock(strIdx); + } + + if (value.dataType != android::Res_value::TYPE_STRING || !pool) { + return {}; + } + return util::getString(*pool, value.data); +} + +Maybe<StringPiece16> ManifestValidator::getAttributeInlineValue(android::ResXMLParser* parser, + size_t idx) { + android::Res_value value; + if (parser->getAttributeValue(idx, &value) < 0) { + return StringPiece16(); + } + + if (value.dataType != android::Res_value::TYPE_STRING) { + return {}; + } + return util::getString(parser->getStrings(), value.data); +} + +bool ManifestValidator::validateInlineAttribute(android::ResXMLParser* parser, size_t idx, + SourceLogger& logger, + const StringPiece16& charSet) { + size_t len = 0; + StringPiece16 element(parser->getElementName(&len), len); + StringPiece16 attributeName(parser->getAttributeName(idx, &len), len); + Maybe<StringPiece16> result = getAttributeInlineValue(parser, idx); + if (!result) { + logger.error(parser->getLineNumber()) + << "<" + << element + << "> must have a '" + << attributeName + << "' attribute with a string literal value." + << std::endl; + return false; + } + return validateAttributeImpl(element, attributeName, result.value(), charSet, + parser->getLineNumber(), logger); +} + +bool ManifestValidator::validateAttribute(android::ResXMLParser* parser, size_t idx, + SourceLogger& logger, const StringPiece16& charSet) { + size_t len = 0; + StringPiece16 element(parser->getElementName(&len), len); + StringPiece16 attributeName(parser->getAttributeName(idx, &len), len); + Maybe<StringPiece16> result = getAttributeValue(parser, idx); + if (!result) { + logger.error(parser->getLineNumber()) + << "<" + << element + << "> must have a '" + << attributeName + << "' attribute that points to a string." + << std::endl; + return false; + } + return validateAttributeImpl(element, attributeName, result.value(), charSet, + parser->getLineNumber(), logger); +} + +bool ManifestValidator::validateAttributeImpl(const StringPiece16& element, + const StringPiece16& attributeName, + const StringPiece16& attributeValue, + const StringPiece16& charSet, size_t lineNumber, + SourceLogger& logger) { + StringPiece16::const_iterator badIter = + util::findNonAlphaNumericAndNotInSet(attributeValue, charSet); + if (badIter != attributeValue.end()) { + logger.error(lineNumber) + << "tag <" + << element + << "> attribute '" + << attributeName + << "' has invalid character '" + << StringPiece16(badIter, 1) + << "'." + << std::endl; + return false; + } + + if (!attributeValue.empty()) { + StringPiece16 trimmed = util::trimWhitespace(attributeValue); + if (attributeValue.begin() != trimmed.begin()) { + logger.error(lineNumber) + << "tag <" + << element + << "> attribute '" + << attributeName + << "' can not start with whitespace." + << std::endl; + return false; + } + + if (attributeValue.end() != trimmed.end()) { + logger.error(lineNumber) + << "tag <" + << element + << "> attribute '" + << attributeName + << "' can not end with whitespace." + << std::endl; + return false; + } + } + return true; +} + +constexpr const char16_t* kPackageIdentSet = u"._"; + +bool ManifestValidator::validateManifest(const Source& source, android::ResXMLParser* parser) { + bool error = false; + SourceLogger logger(source); + + const size_t attrCount = parser->getAttributeCount(); + for (size_t i = 0; i < attrCount; i++) { + size_t len = 0; + StringPiece16 attrNamespace(parser->getAttributeNamespace(i, &len), len); + StringPiece16 attrName(parser->getAttributeName(i, &len), len); + if (attrNamespace.empty() && attrName == u"package") { + error |= !validateInlineAttribute(parser, i, logger, kPackageIdentSet); + } else if (attrNamespace == u"android") { + if (attrName == u"sharedUserId") { + error |= !validateInlineAttribute(parser, i, logger, kPackageIdentSet); + } + } + } + return !error; +} + +} // namespace aapt |
