Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: chrome/browser/android/ntp/popular_sites.cc

Issue 2031603002: In PopularSites, parse (not sanitize) JSON safely. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/android/ntp/popular_sites.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/android/ntp/popular_sites.h" 5 #include "chrome/browser/android/ntp/popular_sites.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/files/file_path.h" 11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
13 #include "base/files/important_file_writer.h" 13 #include "base/files/important_file_writer.h"
14 #include "base/json/json_reader.h" 14 #include "base/json/json_reader.h"
15 #include "base/json/json_writer.h"
15 #include "base/path_service.h" 16 #include "base/path_service.h"
16 #include "base/strings/string_util.h" 17 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h" 18 #include "base/strings/stringprintf.h"
18 #include "base/task_runner_util.h" 19 #include "base/task_runner_util.h"
19 #include "base/time/time.h" 20 #include "base/time/time.h"
20 #include "base/values.h" 21 #include "base/values.h"
21 #include "chrome/common/chrome_paths.h" 22 #include "chrome/common/chrome_paths.h"
22 #include "components/google/core/browser/google_util.h" 23 #include "components/google/core/browser/google_util.h"
23 #include "components/ntp_tiles/pref_names.h" 24 #include "components/ntp_tiles/pref_names.h"
24 #include "components/ntp_tiles/switches.h" 25 #include "components/ntp_tiles/switches.h"
25 #include "components/pref_registry/pref_registry_syncable.h" 26 #include "components/pref_registry/pref_registry_syncable.h"
26 #include "components/prefs/pref_service.h" 27 #include "components/prefs/pref_service.h"
27 #include "components/safe_json/json_sanitizer.h" 28 #include "components/safe_json/safe_json_parser.h"
28 #include "components/search_engines/search_engine_type.h" 29 #include "components/search_engines/search_engine_type.h"
29 #include "components/search_engines/template_url_prepopulate_data.h" 30 #include "components/search_engines/template_url_prepopulate_data.h"
30 #include "components/search_engines/template_url_service.h" 31 #include "components/search_engines/template_url_service.h"
31 #include "components/variations/service/variations_service.h" 32 #include "components/variations/service/variations_service.h"
32 #include "net/base/load_flags.h" 33 #include "net/base/load_flags.h"
33 #include "net/http/http_status_code.h" 34 #include "net/http/http_status_code.h"
34 35
35 using net::URLFetcher; 36 using net::URLFetcher;
36 using variations::VariationsService; 37 using variations::VariationsService;
37 38
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 120
120 if (version.empty()) 121 if (version.empty())
121 version = variation_param_version; 122 version = variation_param_version;
122 123
123 if (version.empty()) 124 if (version.empty())
124 version = kPopularSitesDefaultVersion; 125 version = kPopularSitesDefaultVersion;
125 126
126 return version; 127 return version;
127 } 128 }
128 129
129 std::unique_ptr<std::vector<PopularSites::Site>> ParseJson( 130 // Must run on the blocking thread pool.
130 const std::string& json) { 131 bool WriteJsonToFile(const base::FilePath& local_path,
131 std::unique_ptr<base::Value> value = 132 const base::Value* json) {
132 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS); 133 std::string json_string;
133 base::ListValue* list; 134 return base::JSONWriter::Write(*json, &json_string) &&
134 if (!value || !value->GetAsList(&list)) { 135 base::ImportantFileWriter::WriteFileAtomically(local_path,
135 DLOG(WARNING) << "Failed parsing json"; 136 json_string);
136 return nullptr;
137 }
138
139 std::unique_ptr<std::vector<PopularSites::Site>> sites(
140 new std::vector<PopularSites::Site>);
141 for (size_t i = 0; i < list->GetSize(); i++) {
142 base::DictionaryValue* item;
143 if (!list->GetDictionary(i, &item))
144 continue;
145 base::string16 title;
146 std::string url;
147 if (!item->GetString("title", &title) || !item->GetString("url", &url))
148 continue;
149 std::string favicon_url;
150 item->GetString("favicon_url", &favicon_url);
151 std::string thumbnail_url;
152 item->GetString("thumbnail_url", &thumbnail_url);
153 std::string large_icon_url;
154 item->GetString("large_icon_url", &large_icon_url);
155
156 sites->push_back(PopularSites::Site(title, GURL(url), GURL(favicon_url),
157 GURL(large_icon_url),
158 GURL(thumbnail_url)));
159 }
160
161 return sites;
162 } 137 }
163 138
164 } // namespace 139 } // namespace
165 140
166 base::FilePath ChromePopularSites::GetDirectory() { 141 base::FilePath ChromePopularSites::GetDirectory() {
167 base::FilePath dir; 142 base::FilePath dir;
168 PathService::Get(chrome::DIR_USER_DATA, &dir); 143 PathService::Get(chrome::DIR_USER_DATA, &dir);
169 return dir; // empty if PathService::Get() failed. 144 return dir; // empty if PathService::Get() failed.
170 } 145 }
171 146
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 GURL PopularSites::GetPopularSitesURL() const { 285 GURL PopularSites::GetPopularSitesURL() const {
311 return GURL(base::StringPrintf(kPopularSitesURLFormat, 286 return GURL(base::StringPrintf(kPopularSitesURLFormat,
312 pending_country_.c_str(), 287 pending_country_.c_str(),
313 pending_version_.c_str())); 288 pending_version_.c_str()));
314 } 289 }
315 290
316 void PopularSites::OnReadFileDone(const GURL& url, 291 void PopularSites::OnReadFileDone(const GURL& url,
317 std::unique_ptr<std::string> data, 292 std::unique_ptr<std::string> data,
318 bool success) { 293 bool success) {
319 if (success) { 294 if (success) {
320 ParseSiteList(*data); 295 // Pick the correct overload of JSONReader::Read().
296 using JsonCall = std::unique_ptr<base::Value>(base::StringPiece, int);
297 base::PostTaskAndReplyWithResult(
Marc Treib 2016/06/02 13:00:39 The goal here is to keep the parsing off the UI th
sfiera 2016/06/02 13:12:11 ParseJson() was called on the blocking pool before
Marc Treib 2016/06/02 13:25:20 That seems to be a historical artifact - before ht
sfiera 2016/06/02 13:31:05 Ah, OK, UI thread it is. Starting a dry run now.
298 blocking_runner_.get(), FROM_HERE,
299 base::Bind<JsonCall*>(&base::JSONReader::Read, *data,
300 base::JSON_ALLOW_TRAILING_COMMAS),
301 base::Bind(&PopularSites::OnUnsafeParseDone,
302 weak_ptr_factory_.GetWeakPtr()));
321 } else { 303 } else {
322 // File didn't exist, or couldn't be read for some other reason. 304 // File didn't exist, or couldn't be read for some other reason.
323 FetchPopularSites(url); 305 FetchPopularSites(url);
324 } 306 }
325 } 307 }
326 308
309 void PopularSites::OnUnsafeParseDone(std::unique_ptr<base::Value> json) {
310 if (json) {
311 OnJsonParsed(false, std::move(json));
Marc Treib 2016/06/02 13:00:39 Directly call ParseSiteList here, and remove the w
sfiera 2016/06/02 13:12:11 Done.
312 } else {
313 OnJsonParseFailed("previously-fetched JSON was no longer parseable");
314 }
315 }
316
327 void PopularSites::FetchPopularSites(const GURL& url) { 317 void PopularSites::FetchPopularSites(const GURL& url) {
328 fetcher_ = URLFetcher::Create(url, URLFetcher::GET, this); 318 fetcher_ = URLFetcher::Create(url, URLFetcher::GET, this);
329 fetcher_->SetRequestContext(download_context_); 319 fetcher_->SetRequestContext(download_context_);
330 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | 320 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
331 net::LOAD_DO_NOT_SAVE_COOKIES); 321 net::LOAD_DO_NOT_SAVE_COOKIES);
332 fetcher_->SetAutomaticallyRetryOnNetworkChanges(1); 322 fetcher_->SetAutomaticallyRetryOnNetworkChanges(1);
333 fetcher_->Start(); 323 fetcher_->Start();
334 } 324 }
335 325
336 void PopularSites::OnURLFetchComplete(const net::URLFetcher* source) { 326 void PopularSites::OnURLFetchComplete(const net::URLFetcher* source) {
337 DCHECK_EQ(fetcher_.get(), source); 327 DCHECK_EQ(fetcher_.get(), source);
338 std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_); 328 std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_);
339 329
340 std::string sketchy_json; 330 std::string json_string;
341 if (!(source->GetStatus().is_success() && 331 if (!(source->GetStatus().is_success() &&
342 source->GetResponseCode() == net::HTTP_OK && 332 source->GetResponseCode() == net::HTTP_OK &&
343 source->GetResponseAsString(&sketchy_json))) { 333 source->GetResponseAsString(&json_string))) {
344 OnDownloadFailed(); 334 OnDownloadFailed();
345 return; 335 return;
346 } 336 }
347 337
348 safe_json::JsonSanitizer::Sanitize( 338 safe_json::SafeJsonParser::Parse(
349 sketchy_json, base::Bind(&PopularSites::OnJsonSanitized, 339 json_string,
350 weak_ptr_factory_.GetWeakPtr()), 340 base::Bind(&PopularSites::OnJsonParsed, weak_ptr_factory_.GetWeakPtr(),
351 base::Bind(&PopularSites::OnJsonSanitizationFailed, 341 true /* write_to_file */),
342 base::Bind(&PopularSites::OnJsonParseFailed,
352 weak_ptr_factory_.GetWeakPtr())); 343 weak_ptr_factory_.GetWeakPtr()));
353 } 344 }
354 345
355 void PopularSites::OnJsonSanitized(const std::string& valid_minified_json) { 346 void PopularSites::OnJsonParsed(bool write_to_file,
356 base::PostTaskAndReplyWithResult( 347 std::unique_ptr<base::Value> json) {
357 blocking_runner_.get(), FROM_HERE, 348 if (write_to_file) {
358 base::Bind(&base::ImportantFileWriter::WriteFileAtomically, local_path_, 349 const base::Value* json_ptr = json.get();
359 valid_minified_json), 350 base::PostTaskAndReplyWithResult(
360 base::Bind(&PopularSites::OnFileWriteDone, weak_ptr_factory_.GetWeakPtr(), 351 blocking_runner_.get(), FROM_HERE,
361 valid_minified_json)); 352 base::Bind(&WriteJsonToFile, local_path_, json_ptr),
353 base::Bind(&PopularSites::OnFileWriteDone,
354 weak_ptr_factory_.GetWeakPtr(),
355 base::Passed(std::move(json))));
356 } else {
357 ParseSiteList(std::move(json));
358 }
362 } 359 }
363 360
364 void PopularSites::OnJsonSanitizationFailed(const std::string& error_message) { 361 void PopularSites::OnJsonParseFailed(const std::string& error_message) {
365 DLOG(WARNING) << "JSON sanitization failed: " << error_message; 362 DLOG(WARNING) << "JSON parsing failed: " << error_message;
366 OnDownloadFailed(); 363 OnDownloadFailed();
367 } 364 }
368 365
369 void PopularSites::OnFileWriteDone(const std::string& json, bool success) { 366 void PopularSites::OnFileWriteDone(std::unique_ptr<base::Value> json,
367 bool success) {
370 if (success) { 368 if (success) {
371 prefs_->SetInt64(kPopularSitesLastDownloadPref, 369 prefs_->SetInt64(kPopularSitesLastDownloadPref,
372 base::Time::Now().ToInternalValue()); 370 base::Time::Now().ToInternalValue());
373 prefs_->SetString(kPopularSitesCountryPref, pending_country_); 371 prefs_->SetString(kPopularSitesCountryPref, pending_country_);
374 prefs_->SetString(kPopularSitesVersionPref, pending_version_); 372 prefs_->SetString(kPopularSitesVersionPref, pending_version_);
375 ParseSiteList(json); 373 ParseSiteList(std::move(json));
376 } else { 374 } else {
377 DLOG(WARNING) << "Could not write file to " 375 DLOG(WARNING) << "Could not write file to "
378 << local_path_.LossyDisplayName(); 376 << local_path_.LossyDisplayName();
379 OnDownloadFailed(); 377 OnDownloadFailed();
380 } 378 }
381 } 379 }
382 380
383 void PopularSites::ParseSiteList(const std::string& json) { 381 void PopularSites::ParseSiteList(std::unique_ptr<base::Value> json) {
384 base::PostTaskAndReplyWithResult( 382 base::ListValue* list = nullptr;
385 blocking_runner_.get(), FROM_HERE, base::Bind(&ParseJson, json), 383 if (!json || !json->GetAsList(&list)) {
386 base::Bind(&PopularSites::OnJsonParsed, weak_ptr_factory_.GetWeakPtr())); 384 DLOG(WARNING) << "JSON is not a list";
387 } 385 sites_.clear();
386 callback_.Run(false);
387 return;
388 }
388 389
389 void PopularSites::OnJsonParsed(std::unique_ptr<std::vector<Site>> sites) { 390 std::vector<PopularSites::Site> sites;
390 if (sites) 391 for (size_t i = 0; i < list->GetSize(); i++) {
391 sites_.swap(*sites); 392 base::DictionaryValue* item;
392 else 393 if (!list->GetDictionary(i, &item))
393 sites_.clear(); 394 continue;
394 callback_.Run(!!sites); 395 base::string16 title;
396 std::string url;
397 if (!item->GetString("title", &title) || !item->GetString("url", &url))
398 continue;
399 std::string favicon_url;
400 item->GetString("favicon_url", &favicon_url);
401 std::string thumbnail_url;
402 item->GetString("thumbnail_url", &thumbnail_url);
403 std::string large_icon_url;
404 item->GetString("large_icon_url", &large_icon_url);
405
406 sites.push_back(PopularSites::Site(title, GURL(url), GURL(favicon_url),
407 GURL(large_icon_url),
408 GURL(thumbnail_url)));
409 }
410
411 sites_.swap(sites);
412 callback_.Run(true);
395 } 413 }
396 414
397 void PopularSites::OnDownloadFailed() { 415 void PopularSites::OnDownloadFailed() {
398 if (!is_fallback_) { 416 if (!is_fallback_) {
399 DLOG(WARNING) << "Download country site list failed"; 417 DLOG(WARNING) << "Download country site list failed";
400 is_fallback_ = true; 418 is_fallback_ = true;
401 pending_country_ = kPopularSitesDefaultCountryCode; 419 pending_country_ = kPopularSitesDefaultCountryCode;
402 pending_version_ = kPopularSitesDefaultVersion; 420 pending_version_ = kPopularSitesDefaultVersion;
403 FetchPopularSites(GetPopularSitesURL()); 421 FetchPopularSites(GetPopularSitesURL());
404 } else { 422 } else {
405 DLOG(WARNING) << "Download fallback site list failed"; 423 DLOG(WARNING) << "Download fallback site list failed";
406 callback_.Run(false); 424 callback_.Run(false);
407 } 425 }
408 } 426 }
OLDNEW
« no previous file with comments | « chrome/browser/android/ntp/popular_sites.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698