1 //
2 // Copyright (C) 2019 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 "update_engine/omaha_request_builder_xml.h"
18
19 #include <string>
20 #include <utility>
21 #include <vector>
22
23 #include <base/guid.h>
24 #include <gtest/gtest.h>
25
26 #include "update_engine/fake_system_state.h"
27
28 using std::pair;
29 using std::string;
30 using std::vector;
31
32 namespace chromeos_update_engine {
33
34 namespace {
35 // Helper to find key and extract value from the given string |xml|, instead
36 // of using a full parser. The attribute key will be followed by "=\"" as xml
37 // attribute values must be within double quotes (not single quotes).
FindAttributeKeyValueInXml(const string & xml,const string & key,const size_t val_size)38 static string FindAttributeKeyValueInXml(const string& xml,
39 const string& key,
40 const size_t val_size) {
41 string key_with_quotes = key + "=\"";
42 const size_t val_start_pos = xml.find(key);
43 if (val_start_pos == string::npos)
44 return "";
45 return xml.substr(val_start_pos + key_with_quotes.size(), val_size);
46 }
47 // Helper to find the count of substring in a string.
CountSubstringInString(const string & str,const string & substr)48 static size_t CountSubstringInString(const string& str, const string& substr) {
49 size_t count = 0, pos = 0;
50 while ((pos = str.find(substr, pos ? pos + 1 : 0)) != string::npos)
51 ++count;
52 return count;
53 }
54 } // namespace
55
56 class OmahaRequestBuilderXmlTest : public ::testing::Test {
57 protected:
SetUp()58 void SetUp() override {}
TearDown()59 void TearDown() override {}
60
61 FakeSystemState fake_system_state_;
62 static constexpr size_t kGuidSize = 36;
63 };
64
TEST_F(OmahaRequestBuilderXmlTest,XmlEncodeTest)65 TEST_F(OmahaRequestBuilderXmlTest, XmlEncodeTest) {
66 string output;
67 vector<pair<string, string>> xml_encode_pairs = {
68 {"ab", "ab"},
69 {"a<b", "a<b"},
70 {"<&>\"\'\\", "<&>"'\\"},
71 {"<&>", "&lt;&amp;&gt;"}};
72 for (const auto& xml_encode_pair : xml_encode_pairs) {
73 const auto& before_encoding = xml_encode_pair.first;
74 const auto& after_encoding = xml_encode_pair.second;
75 EXPECT_TRUE(XmlEncode(before_encoding, &output));
76 EXPECT_EQ(after_encoding, output);
77 }
78 // Check that unterminated UTF-8 strings are handled properly.
79 EXPECT_FALSE(XmlEncode("\xc2", &output));
80 // Fail with invalid ASCII-7 chars.
81 EXPECT_FALSE(XmlEncode("This is an 'n' with a tilde: \xc3\xb1", &output));
82 }
83
TEST_F(OmahaRequestBuilderXmlTest,XmlEncodeWithDefaultTest)84 TEST_F(OmahaRequestBuilderXmlTest, XmlEncodeWithDefaultTest) {
85 EXPECT_EQ("", XmlEncodeWithDefault(""));
86 EXPECT_EQ("<&>", XmlEncodeWithDefault("<&>", "something else"));
87 EXPECT_EQ("<not escaped>", XmlEncodeWithDefault("\xc2", "<not escaped>"));
88 }
89
TEST_F(OmahaRequestBuilderXmlTest,PlatformGetAppTest)90 TEST_F(OmahaRequestBuilderXmlTest, PlatformGetAppTest) {
91 OmahaRequestParams omaha_request_params{&fake_system_state_};
92 omaha_request_params.set_device_requisition("device requisition");
93 OmahaRequestBuilderXml omaha_request{nullptr,
94 &omaha_request_params,
95 false,
96 false,
97 0,
98 0,
99 0,
100 fake_system_state_.prefs(),
101 ""};
102 OmahaAppData dlc_app_data = {.id = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
103 .version = "",
104 .skip_update = false,
105 .is_dlc = false};
106
107 // Verify that the attributes that shouldn't be missing for Platform AppID are
108 // in fact present in the <app ...></app>.
109 const string app = omaha_request.GetApp(dlc_app_data);
110 EXPECT_NE(string::npos, app.find("lang="));
111 EXPECT_NE(string::npos, app.find("fw_version="));
112 EXPECT_NE(string::npos, app.find("ec_version="));
113 EXPECT_NE(string::npos, app.find("requisition="));
114 }
115
TEST_F(OmahaRequestBuilderXmlTest,DlcGetAppTest)116 TEST_F(OmahaRequestBuilderXmlTest, DlcGetAppTest) {
117 OmahaRequestParams omaha_request_params{&fake_system_state_};
118 omaha_request_params.set_device_requisition("device requisition");
119 OmahaRequestBuilderXml omaha_request{nullptr,
120 &omaha_request_params,
121 false,
122 false,
123 0,
124 0,
125 0,
126 fake_system_state_.prefs(),
127 ""};
128 OmahaAppData dlc_app_data = {
129 .id = "_dlc_id", .version = "", .skip_update = false, .is_dlc = true};
130
131 // Verify that the attributes that should be missing for DLC AppIDs are in
132 // fact not present in the <app ...></app>.
133 const string app = omaha_request.GetApp(dlc_app_data);
134 EXPECT_EQ(string::npos, app.find("lang="));
135 EXPECT_EQ(string::npos, app.find("fw_version="));
136 EXPECT_EQ(string::npos, app.find("ec_version="));
137 EXPECT_EQ(string::npos, app.find("requisition="));
138 }
139
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlRequestIdTest)140 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlRequestIdTest) {
141 OmahaRequestParams omaha_request_params{&fake_system_state_};
142 OmahaRequestBuilderXml omaha_request{nullptr,
143 &omaha_request_params,
144 false,
145 false,
146 0,
147 0,
148 0,
149 fake_system_state_.prefs(),
150 ""};
151 const string request_xml = omaha_request.GetRequest();
152 const string key = "requestid";
153 const string request_id =
154 FindAttributeKeyValueInXml(request_xml, key, kGuidSize);
155 // A valid |request_id| is either a GUID version 4 or empty string.
156 if (!request_id.empty())
157 EXPECT_TRUE(base::IsValidGUID(request_id));
158 }
159
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlSessionIdTest)160 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlSessionIdTest) {
161 const string gen_session_id = base::GenerateGUID();
162 OmahaRequestParams omaha_request_params{&fake_system_state_};
163 OmahaRequestBuilderXml omaha_request{nullptr,
164 &omaha_request_params,
165 false,
166 false,
167 0,
168 0,
169 0,
170 fake_system_state_.prefs(),
171 gen_session_id};
172 const string request_xml = omaha_request.GetRequest();
173 const string key = "sessionid";
174 const string session_id =
175 FindAttributeKeyValueInXml(request_xml, key, kGuidSize);
176 // A valid |session_id| is either a GUID version 4 or empty string.
177 if (!session_id.empty()) {
178 EXPECT_TRUE(base::IsValidGUID(session_id));
179 }
180 EXPECT_EQ(gen_session_id, session_id);
181 }
182
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlPlatformUpdateTest)183 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlPlatformUpdateTest) {
184 OmahaRequestParams omaha_request_params{&fake_system_state_};
185 OmahaRequestBuilderXml omaha_request{nullptr,
186 &omaha_request_params,
187 false,
188 false,
189 0,
190 0,
191 0,
192 fake_system_state_.prefs(),
193 ""};
194 const string request_xml = omaha_request.GetRequest();
195 EXPECT_EQ(1, CountSubstringInString(request_xml, "<updatecheck"))
196 << request_xml;
197 }
198
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlPlatformUpdateWithDlcsTest)199 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlPlatformUpdateWithDlcsTest) {
200 OmahaRequestParams omaha_request_params{&fake_system_state_};
201 omaha_request_params.set_dlc_apps_params(
202 {{omaha_request_params.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
203 {omaha_request_params.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
204 OmahaRequestBuilderXml omaha_request{nullptr,
205 &omaha_request_params,
206 false,
207 false,
208 0,
209 0,
210 0,
211 fake_system_state_.prefs(),
212 ""};
213 const string request_xml = omaha_request.GetRequest();
214 EXPECT_EQ(3, CountSubstringInString(request_xml, "<updatecheck"))
215 << request_xml;
216 }
217
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlDlcInstallationTest)218 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcInstallationTest) {
219 OmahaRequestParams omaha_request_params{&fake_system_state_};
220 const std::map<std::string, OmahaRequestParams::AppParams> dlcs = {
221 {omaha_request_params.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
222 {omaha_request_params.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}};
223 omaha_request_params.set_dlc_apps_params(dlcs);
224 omaha_request_params.set_is_install(true);
225 OmahaRequestBuilderXml omaha_request{nullptr,
226 &omaha_request_params,
227 false,
228 false,
229 0,
230 0,
231 0,
232 fake_system_state_.prefs(),
233 ""};
234 const string request_xml = omaha_request.GetRequest();
235 EXPECT_EQ(2, CountSubstringInString(request_xml, "<updatecheck"))
236 << request_xml;
237
238 auto FindAppId = [request_xml](size_t pos) -> size_t {
239 return request_xml.find("<app appid", pos);
240 };
241 // Skip over the Platform AppID, which is always first.
242 size_t pos = FindAppId(0);
243 for (auto&& _ : dlcs) {
244 (void)_;
245 EXPECT_NE(string::npos, (pos = FindAppId(pos + 1))) << request_xml;
246 const string dlc_app_id_version = FindAttributeKeyValueInXml(
247 request_xml.substr(pos), "version", string(kNoVersion).size());
248 EXPECT_EQ(kNoVersion, dlc_app_id_version);
249
250 const string false_str = "false";
251 const string dlc_app_id_delta_okay = FindAttributeKeyValueInXml(
252 request_xml.substr(pos), "delta_okay", false_str.length());
253 EXPECT_EQ(false_str, dlc_app_id_delta_okay);
254 }
255 }
256
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlDlcNoPing)257 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcNoPing) {
258 OmahaRequestParams omaha_request_params{&fake_system_state_};
259 omaha_request_params.set_dlc_apps_params(
260 {{omaha_request_params.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}}});
261 OmahaRequestBuilderXml omaha_request{nullptr,
262 &omaha_request_params,
263 false,
264 false,
265 0,
266 0,
267 0,
268 fake_system_state_.prefs(),
269 ""};
270 const string request_xml = omaha_request.GetRequest();
271 EXPECT_EQ(0, CountSubstringInString(request_xml, "<ping")) << request_xml;
272 }
273
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlDlcPingRollCallNoActive)274 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallNoActive) {
275 OmahaRequestParams omaha_request_params{&fake_system_state_};
276 omaha_request_params.set_dlc_apps_params(
277 {{omaha_request_params.GetDlcAppId("dlc_no_0"),
278 {.active_counting_type = OmahaRequestParams::kDateBased,
279 .name = "dlc_no_0",
280 .ping_date_last_active = 25,
281 .ping_date_last_rollcall = 36,
282 .send_ping = true}}});
283 OmahaRequestBuilderXml omaha_request{nullptr,
284 &omaha_request_params,
285 false,
286 false,
287 0,
288 0,
289 0,
290 fake_system_state_.prefs(),
291 ""};
292 const string request_xml = omaha_request.GetRequest();
293 EXPECT_EQ(1, CountSubstringInString(request_xml, "<ping rd=\"36\""))
294 << request_xml;
295 }
296
TEST_F(OmahaRequestBuilderXmlTest,GetRequestXmlDlcPingRollCallAndActive)297 TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallAndActive) {
298 OmahaRequestParams omaha_request_params{&fake_system_state_};
299 omaha_request_params.set_dlc_apps_params(
300 {{omaha_request_params.GetDlcAppId("dlc_no_0"),
301 {.active_counting_type = OmahaRequestParams::kDateBased,
302 .name = "dlc_no_0",
303 .ping_active = 1,
304 .ping_date_last_active = 25,
305 .ping_date_last_rollcall = 36,
306 .send_ping = true}}});
307 OmahaRequestBuilderXml omaha_request{nullptr,
308 &omaha_request_params,
309 false,
310 false,
311 0,
312 0,
313 0,
314 fake_system_state_.prefs(),
315 ""};
316 const string request_xml = omaha_request.GetRequest();
317 EXPECT_EQ(1,
318 CountSubstringInString(request_xml,
319 "<ping active=\"1\" ad=\"25\" rd=\"36\""))
320 << request_xml;
321 }
322 } // namespace chromeos_update_engine
323