2014-11-14 14:48:12 -08:00
|
|
|
/*
|
|
|
|
* 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 '"
|
2015-04-03 12:08:26 -07:00
|
|
|
<< StringPiece16(badIter, 1)
|
2014-11-14 14:48:12 -08:00
|
|
|
<< "'."
|
|
|
|
<< 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
|