protocol_parser_json.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/update_client/protocol_parser_json.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/json/json_reader.h"
  8. #include "base/strings/strcat.h"
  9. #include "base/strings/string_util.h"
  10. #include "base/values.h"
  11. #include "base/version.h"
  12. #include "components/update_client/protocol_definition.h"
  13. namespace update_client {
  14. namespace {
  15. std::string GetValueString(const base::Value& node, const char* key) {
  16. const auto* value = node.FindKey(key);
  17. return (value && value->is_string()) ? value->GetString() : std::string();
  18. }
  19. bool ParseManifest(const base::Value& manifest_node,
  20. ProtocolParser::Result* result,
  21. std::string* error) {
  22. if (!manifest_node.is_dict()) {
  23. *error = "'manifest' is not a dictionary.";
  24. }
  25. const auto* version = manifest_node.FindKey("version");
  26. if (!version || !version->is_string()) {
  27. *error = "Missing version for manifest.";
  28. return false;
  29. }
  30. result->manifest.version = version->GetString();
  31. if (!base::Version(result->manifest.version).IsValid()) {
  32. *error =
  33. base::StrCat({"Invalid version: '", result->manifest.version, "'."});
  34. return false;
  35. }
  36. // Get the optional minimum browser version.
  37. const auto* browser_min_version = manifest_node.FindKey("prodversionmin");
  38. if (browser_min_version && browser_min_version->is_string()) {
  39. result->manifest.browser_min_version = browser_min_version->GetString();
  40. if (!base::Version(result->manifest.browser_min_version).IsValid()) {
  41. *error = base::StrCat({"Invalid prodversionmin: '",
  42. result->manifest.browser_min_version, "'."});
  43. return false;
  44. }
  45. }
  46. result->manifest.run = GetValueString(manifest_node, "run");
  47. result->manifest.arguments = GetValueString(manifest_node, "arguments");
  48. const auto* packages_node = manifest_node.FindKey("packages");
  49. if (!packages_node || !packages_node->is_dict()) {
  50. *error = "Missing packages in manifest or 'packages' is not a dictionary.";
  51. return false;
  52. }
  53. const auto* package_node = packages_node->FindKey("package");
  54. if (!package_node || !package_node->is_list()) {
  55. *error = "Missing package in packages.";
  56. return false;
  57. }
  58. for (const auto& package : package_node->GetListDeprecated()) {
  59. if (!package.is_dict()) {
  60. *error = "'package' is not a dictionary.";
  61. return false;
  62. }
  63. ProtocolParser::Result::Manifest::Package p;
  64. const auto* name = package.FindKey("name");
  65. if (!name || !name->is_string()) {
  66. *error = "Missing name for package.";
  67. return false;
  68. }
  69. p.name = name->GetString();
  70. p.namediff = GetValueString(package, "namediff");
  71. p.fingerprint = GetValueString(package, "fp");
  72. p.hash_sha256 = GetValueString(package, "hash_sha256");
  73. p.hashdiff_sha256 = GetValueString(package, "hashdiff_sha256");
  74. const auto* size = package.FindKey("size");
  75. if (size && (size->is_int() || size->is_double())) {
  76. const auto val = size->GetDouble();
  77. if (0 <= val && val < kProtocolMaxInt)
  78. p.size = size->GetDouble();
  79. }
  80. const auto* sizediff = package.FindKey("sizediff");
  81. if (sizediff && (sizediff->is_int() || sizediff->is_double())) {
  82. const auto val = sizediff->GetDouble();
  83. if (0 <= val && val < kProtocolMaxInt)
  84. p.sizediff = sizediff->GetDouble();
  85. }
  86. result->manifest.packages.push_back(std::move(p));
  87. }
  88. return true;
  89. }
  90. void ParseActions(const base::Value& actions_node,
  91. ProtocolParser::Result* result) {
  92. if (!actions_node.is_dict())
  93. return;
  94. const auto* action_node = actions_node.FindKey("action");
  95. if (!action_node || !action_node->is_list())
  96. return;
  97. const auto& action_list = action_node->GetListDeprecated();
  98. if (action_list.empty() || !action_list[0].is_dict())
  99. return;
  100. result->action_run = GetValueString(action_list[0], "run");
  101. }
  102. bool ParseUrls(const base::Value& urls_node,
  103. ProtocolParser::Result* result,
  104. std::string* error) {
  105. if (!urls_node.is_dict()) {
  106. *error = "'urls' is not a dictionary.";
  107. return false;
  108. }
  109. const auto* url_node = urls_node.FindKey("url");
  110. if (!url_node || !url_node->is_list()) {
  111. *error = "Missing url on urls.";
  112. return false;
  113. }
  114. for (const auto& url : url_node->GetListDeprecated()) {
  115. if (!url.is_dict())
  116. continue;
  117. const auto* codebase = url.FindKey("codebase");
  118. if (codebase && codebase->is_string()) {
  119. GURL crx_url(codebase->GetString());
  120. if (crx_url.is_valid())
  121. result->crx_urls.push_back(std::move(crx_url));
  122. }
  123. const auto* codebasediff = url.FindKey("codebasediff");
  124. if (codebasediff && codebasediff->is_string()) {
  125. GURL crx_diffurl(codebasediff->GetString());
  126. if (crx_diffurl.is_valid())
  127. result->crx_diffurls.push_back(std::move(crx_diffurl));
  128. }
  129. }
  130. // Expect at least one url for full update.
  131. if (result->crx_urls.empty()) {
  132. *error = "Missing valid url for full update.";
  133. return false;
  134. }
  135. return true;
  136. }
  137. void ParseData(const base::Value& data_node, ProtocolParser::Result* result) {
  138. if (!data_node.is_dict())
  139. return;
  140. result->data.emplace_back(ProtocolParser::Result::Data(
  141. GetValueString(data_node, "status"), GetValueString(data_node, "name"),
  142. GetValueString(data_node, "index"), GetValueString(data_node, "#text")));
  143. }
  144. bool ParseUpdateCheck(const base::Value& updatecheck_node,
  145. ProtocolParser::Result* result,
  146. std::string* error) {
  147. if (!updatecheck_node.is_dict()) {
  148. *error = "'updatecheck' is not a dictionary.";
  149. return false;
  150. }
  151. for (auto kv : updatecheck_node.DictItems()) {
  152. if (kv.first.front() == '_' && kv.second.is_string()) {
  153. result->custom_attributes[kv.first] = kv.second.GetString();
  154. }
  155. }
  156. const auto* status = updatecheck_node.FindKey("status");
  157. if (!status || !status->is_string()) {
  158. *error = "Missing status on updatecheck node";
  159. return false;
  160. }
  161. result->status = status->GetString();
  162. if (result->status == "noupdate") {
  163. const auto* actions_node = updatecheck_node.FindKey("actions");
  164. if (actions_node)
  165. ParseActions(*actions_node, result);
  166. return true;
  167. }
  168. if (result->status == "ok") {
  169. const auto* actions_node = updatecheck_node.FindKey("actions");
  170. if (actions_node)
  171. ParseActions(*actions_node, result);
  172. const auto* urls_node = updatecheck_node.FindKey("urls");
  173. if (!urls_node) {
  174. *error = "Missing urls on updatecheck.";
  175. return false;
  176. }
  177. if (!ParseUrls(*urls_node, result, error))
  178. return false;
  179. const auto* manifest_node = updatecheck_node.FindKey("manifest");
  180. if (!manifest_node) {
  181. *error = "Missing manifest on updatecheck.";
  182. return false;
  183. }
  184. return ParseManifest(*manifest_node, result, error);
  185. }
  186. // Return the |updatecheck| element status as a parsing error.
  187. *error = result->status;
  188. return false;
  189. }
  190. bool ParseApp(const base::Value& app_node,
  191. ProtocolParser::Result* result,
  192. std::string* error) {
  193. if (!app_node.is_dict()) {
  194. *error = "'app' is not a dictionary.";
  195. return false;
  196. }
  197. for (const auto* cohort_key :
  198. {ProtocolParser::Result::kCohort, ProtocolParser::Result::kCohortHint,
  199. ProtocolParser::Result::kCohortName}) {
  200. const auto* cohort_value = app_node.FindKey(cohort_key);
  201. if (cohort_value && cohort_value->is_string())
  202. result->cohort_attrs[cohort_key] = cohort_value->GetString();
  203. }
  204. const auto* appid = app_node.FindKey("appid");
  205. if (appid && appid->is_string())
  206. result->extension_id = appid->GetString();
  207. if (result->extension_id.empty()) {
  208. *error = "Missing appid on app node";
  209. return false;
  210. }
  211. // Read the |status| attribute for the app.
  212. // If the status is one of the defined app status error literals, then return
  213. // it in the result as if it were an updatecheck status, then stop parsing,
  214. // and return success.
  215. const auto* status = app_node.FindKey("status");
  216. if (status && status->is_string()) {
  217. result->status = status->GetString();
  218. if (result->status == "restricted" ||
  219. result->status == "error-unknownApplication" ||
  220. result->status == "error-invalidAppId")
  221. return true;
  222. // If the status was not handled above and the status is not "ok", then
  223. // this must be a status literal that that the parser does not know about.
  224. if (!result->status.empty() && result->status != "ok") {
  225. *error = "Unknown app status";
  226. return false;
  227. }
  228. }
  229. DCHECK(result->status.empty() || result->status == "ok");
  230. if (const auto* data_node = app_node.FindKey("data")) {
  231. if (const auto* data_list = data_node->GetIfList()) {
  232. std::for_each(
  233. data_list->begin(), data_list->end(),
  234. [&result](const base::Value& data) { ParseData(data, result); });
  235. }
  236. }
  237. const auto* updatecheck_node = app_node.FindKey("updatecheck");
  238. if (!updatecheck_node) {
  239. *error = "Missing updatecheck on app.";
  240. return false;
  241. }
  242. return ParseUpdateCheck(*updatecheck_node, result, error);
  243. }
  244. } // namespace
  245. bool ProtocolParserJSON::DoParse(const std::string& response_json,
  246. Results* results) {
  247. DCHECK(results);
  248. if (response_json.empty()) {
  249. ParseError("Empty JSON.");
  250. return false;
  251. }
  252. // The JSON response contains a prefix to prevent XSSI.
  253. constexpr char kJSONPrefix[] = ")]}'";
  254. if (!base::StartsWith(response_json, kJSONPrefix,
  255. base::CompareCase::SENSITIVE)) {
  256. ParseError("Missing secure JSON prefix.");
  257. return false;
  258. }
  259. const auto doc = base::JSONReader::Read(base::MakeStringPiece(
  260. response_json.begin() + std::char_traits<char>::length(kJSONPrefix),
  261. response_json.end()));
  262. if (!doc) {
  263. ParseError("JSON read error.");
  264. return false;
  265. }
  266. if (!doc->is_dict()) {
  267. ParseError("JSON document is not a dictionary.");
  268. return false;
  269. }
  270. const auto* response_node = doc->FindKey("response");
  271. if (!response_node || !response_node->is_dict()) {
  272. ParseError("Missing 'response' element or 'response' is not a dictionary.");
  273. return false;
  274. }
  275. const auto* protocol = response_node->FindKey("protocol");
  276. if (!protocol || !protocol->is_string()) {
  277. ParseError("Missing/non-string protocol.");
  278. return false;
  279. }
  280. if (protocol->GetString() != kProtocolVersion) {
  281. ParseError("Incorrect protocol. (expected '%s', found '%s')",
  282. kProtocolVersion, protocol->GetString().c_str());
  283. return false;
  284. }
  285. const auto* daystart_node = response_node->FindKey("daystart");
  286. if (daystart_node && daystart_node->is_dict()) {
  287. const auto* elapsed_seconds = daystart_node->FindKey("elapsed_seconds");
  288. if (elapsed_seconds && elapsed_seconds->is_int())
  289. results->daystart_elapsed_seconds = elapsed_seconds->GetInt();
  290. const auto* elapsed_days = daystart_node->FindKey("elapsed_days");
  291. if (elapsed_days && elapsed_days->is_int())
  292. results->daystart_elapsed_days = elapsed_days->GetInt();
  293. }
  294. const auto* app_node = response_node->FindKey("app");
  295. if (app_node && app_node->is_list()) {
  296. for (const auto& app : app_node->GetListDeprecated()) {
  297. Result result;
  298. std::string error;
  299. if (ParseApp(app, &result, &error))
  300. results->list.push_back(result);
  301. else
  302. ParseError("%s", error.c_str());
  303. }
  304. }
  305. return true;
  306. }
  307. } // namespace update_client