blob: 123b9fae2fbaf8b5913994176d3da6b6fa9cf1a4 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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 '"
Adam Lesinskica2fc352015-04-03 12:08:26 -0700154 << StringPiece16(badIter, 1)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800155 << "'."
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
Adam Lesinski24aad162015-04-24 19:19:30 -0700193 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);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800213 }
214 return !error;
215}
216
217} // namespace aapt