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 
25 namespace aapt {
26 
ManifestValidator(const android::ResTable & table)27 ManifestValidator::ManifestValidator(const android::ResTable& table)
28 : mTable(table) {
29 }
30 
validate(const Source & source,android::ResXMLParser * parser)31 bool 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 
getAttributeValue(android::ResXMLParser * parser,size_t idx)64 Maybe<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 
getAttributeInlineValue(android::ResXMLParser * parser,size_t idx)86 Maybe<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 
validateInlineAttribute(android::ResXMLParser * parser,size_t idx,SourceLogger & logger,const StringPiece16 & charSet)99 bool 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 
validateAttribute(android::ResXMLParser * parser,size_t idx,SourceLogger & logger,const StringPiece16 & charSet)120 bool 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 
validateAttributeImpl(const StringPiece16 & element,const StringPiece16 & attributeName,const StringPiece16 & attributeValue,const StringPiece16 & charSet,size_t lineNumber,SourceLogger & logger)140 bool 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 
187 constexpr const char16_t* kPackageIdentSet = u"._";
188 
validateManifest(const Source & source,android::ResXMLParser * parser)189 bool 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