Commit 9d594ea6 authored by Michael Martis's avatar Michael Martis Committed by Commit Bot

Image annotation service: use dedicated JSON parsing service for safety.

This CL switches out our usage of base::JSONReader for the data decoder service
(in another process on desktop Chrome), which is safer for parsing JSON
returned from an external server.

Bug: 916420
Change-Id: I3cb9b20af19064cfa877d88f06baeb417322f0a0
Reviewed-on: https://chromium-review.googlesource.com/c/1491068Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarAndrew Moylan <amoylan@chromium.org>
Reviewed-by: default avatarSam McNally <sammc@chromium.org>
Commit-Queue: Michael Martis <martis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636608}
parent 4f4df5f4
...@@ -15,8 +15,10 @@ source_set("lib") { ...@@ -15,8 +15,10 @@ source_set("lib") {
"//components/google/core/common", "//components/google/core/common",
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//net", "//net",
"//services/data_decoder/public/mojom",
"//services/image_annotation/public/mojom", "//services/image_annotation/public/mojom",
"//services/network/public/cpp", "//services/network/public/cpp",
"//services/service_manager/public/cpp",
"//url", "//url",
] ]
} }
...@@ -51,10 +53,14 @@ source_set("tests") { ...@@ -51,10 +53,14 @@ source_set("tests") {
"//base/test:test_support", "//base/test:test_support",
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//net", "//net",
"//services/data_decoder/public/cpp:test_support",
"//services/data_decoder/public/mojom",
"//services/image_annotation/public/cpp", "//services/image_annotation/public/cpp",
"//services/image_annotation/public/mojom", "//services/image_annotation/public/mojom",
"//services/network:test_support", "//services/network:test_support",
"//services/network/public/cpp", "//services/network/public/cpp",
"//services/service_manager/public/cpp",
"//services/service_manager/public/cpp/test:test_support",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
"//url", "//url",
......
include_rules = [ include_rules = [
"+components/google", "+components/google",
"+net", "+net",
"+services/data_decoder",
"+services/network", "+services/network",
"+third_party/skia", "+third_party/skia",
"+ui/gfx", "+ui/gfx",
......
...@@ -11,13 +11,14 @@ ...@@ -11,13 +11,14 @@
#include "base/base64.h" #include "base/base64.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h" #include "base/feature_list.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/location.h" #include "base/location.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "components/google/core/common/google_util.h" #include "components/google/core/common/google_util.h"
#include "net/base/load_flags.h" #include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/data_decoder/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace image_annotation { namespace image_annotation {
...@@ -153,18 +154,13 @@ std::tuple<bool, std::vector<mojom::AnnotationPtr>> ParseJsonDescAnnotations( ...@@ -153,18 +154,13 @@ std::tuple<bool, std::vector<mojom::AnnotationPtr>> ParseJsonDescAnnotations(
// Attempts to extract annotation results from the server response, returning a // Attempts to extract annotation results from the server response, returning a
// map from each source ID to its annotations (if successfully extracted). // map from each source ID to its annotations (if successfully extracted).
std::map<std::string, mojom::AnnotateImageResultPtr> ParseJsonResponse( std::map<std::string, mojom::AnnotateImageResultPtr> UnpackJsonResponse(
const std::string* const json_response, const base::Value& json_data,
const double min_ocr_confidence) { const double min_ocr_confidence) {
if (!json_response) if (!json_data.is_dict())
return {}; return {};
const base::Optional<base::Value> response = const base::Value* const results = json_data.FindKey("results");
base::JSONReader::Read(*json_response);
if (!response.has_value() || !response->is_dict())
return {};
const base::Value* const results = response->FindKey("results");
if (!results || !results->is_list()) if (!results || !results->is_list())
return {}; return {};
...@@ -240,8 +236,10 @@ Annotator::Annotator( ...@@ -240,8 +236,10 @@ Annotator::Annotator(
const base::TimeDelta throttle, const base::TimeDelta throttle,
const int batch_size, const int batch_size,
const double min_ocr_confidence, const double min_ocr_confidence,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
service_manager::Connector* const connector)
: url_loader_factory_(std::move(url_loader_factory)), : url_loader_factory_(std::move(url_loader_factory)),
connector_(connector),
http_request_timer_( http_request_timer_(
FROM_HERE, FROM_HERE,
throttle, throttle,
...@@ -250,7 +248,9 @@ Annotator::Annotator( ...@@ -250,7 +248,9 @@ Annotator::Annotator(
server_url_(std::move(server_url)), server_url_(std::move(server_url)),
api_key_(std::move(api_key)), api_key_(std::move(api_key)),
batch_size_(batch_size), batch_size_(batch_size),
min_ocr_confidence_(min_ocr_confidence) {} min_ocr_confidence_(min_ocr_confidence) {
DCHECK(connector_);
}
Annotator::~Annotator() {} Annotator::~Annotator() {}
...@@ -465,10 +465,37 @@ void Annotator::OnServerResponseReceived( ...@@ -465,10 +465,37 @@ void Annotator::OnServerResponseReceived(
const std::unique_ptr<std::string> json_response) { const std::unique_ptr<std::string> json_response) {
http_requests_.erase(http_request_it); http_requests_.erase(http_request_it);
if (!json_response) {
DVLOG(1) << "HTTP request to image annotation server failed.";
ProcessResults(source_ids, {});
return;
}
// Send JSON string to a dedicated service for safe parsing.
GetJsonParser().Parse(*json_response,
base::BindOnce(&Annotator::OnResponseJsonParsed,
base::Unretained(this), source_ids));
}
void Annotator::OnResponseJsonParsed(
const std::set<std::string>& source_ids,
const base::Optional<base::Value> json_data,
const base::Optional<std::string>& error) {
// Extract annotation results for each source ID with valid results. // Extract annotation results for each source ID with valid results.
const std::map<std::string, mojom::AnnotateImageResultPtr> results = std::map<std::string, mojom::AnnotateImageResultPtr> results;
ParseJsonResponse(json_response.get(), min_ocr_confidence_); if (!json_data.has_value() || error.has_value()) {
DVLOG(1) << "Parsing server response JSON failed with error: "
<< error.value_or("No reason reported.");
ProcessResults(source_ids, {});
} else {
ProcessResults(source_ids,
UnpackJsonResponse(*json_data, min_ocr_confidence_));
}
}
void Annotator::ProcessResults(
const std::set<std::string>& source_ids,
const std::map<std::string, mojom::AnnotateImageResultPtr>& results) {
// Process each source ID for which we expect to have results. // Process each source ID for which we expect to have results.
for (const std::string& source_id : source_ids) { for (const std::string& source_id : source_ids) {
pending_source_ids_.erase(source_id); pending_source_ids_.erase(source_id);
...@@ -502,6 +529,18 @@ void Annotator::OnServerResponseReceived( ...@@ -502,6 +529,18 @@ void Annotator::OnServerResponseReceived(
} }
} }
data_decoder::mojom::JsonParser& Annotator::GetJsonParser() {
if (!json_parser_) {
connector_->BindInterface(data_decoder::mojom::kServiceName,
mojo::MakeRequest(&json_parser_));
json_parser_.set_connection_error_handler(base::BindOnce(
[](Annotator* const annotator) { annotator->json_parser_.reset(); },
base::Unretained(this)));
}
return *json_parser_;
}
void Annotator::RemoveRequestInfo( void Annotator::RemoveRequestInfo(
const std::string& source_id, const std::string& source_id,
const RequestInfoList::iterator request_info_it, const RequestInfoList::iterator request_info_it,
......
...@@ -18,11 +18,16 @@ ...@@ -18,11 +18,16 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/bindings/binding_set.h"
#include "services/data_decoder/public/mojom/json_parser.mojom.h"
#include "services/image_annotation/public/mojom/image_annotation.mojom.h" #include "services/image_annotation/public/mojom/image_annotation.mojom.h"
#include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h" #include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace service_manager {
class Connector;
} // namespace service_manager
namespace image_annotation { namespace image_annotation {
// The annotator communicates with the external image annotation server to // The annotator communicates with the external image annotation server to
...@@ -61,7 +66,8 @@ class Annotator : public mojom::Annotator { ...@@ -61,7 +66,8 @@ class Annotator : public mojom::Annotator {
base::TimeDelta throttle, base::TimeDelta throttle,
int batch_size, int batch_size,
double min_ocr_confidence, double min_ocr_confidence,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
service_manager::Connector* connector);
~Annotator() override; ~Annotator() override;
// Start providing behavior for the given Mojo request. // Start providing behavior for the given Mojo request.
...@@ -101,6 +107,10 @@ class Annotator : public mojom::Annotator { ...@@ -101,6 +107,10 @@ class Annotator : public mojom::Annotator {
HttpRequestQueue::iterator begin_it, HttpRequestQueue::iterator begin_it,
HttpRequestQueue::iterator end_it); HttpRequestQueue::iterator end_it);
// Create or reuse a connection to the data decoder service for safe JSON
// parsing.
data_decoder::mojom::JsonParser& GetJsonParser();
// Removes the given request, reassigning local processing if its associated // Removes the given request, reassigning local processing if its associated
// image processor had some ongoing. // image processor had some ongoing.
void RemoveRequestInfo(const std::string& source_id, void RemoveRequestInfo(const std::string& source_id,
...@@ -123,6 +133,17 @@ class Annotator : public mojom::Annotator { ...@@ -123,6 +133,17 @@ class Annotator : public mojom::Annotator {
UrlLoaderList::iterator http_request_it, UrlLoaderList::iterator http_request_it,
std::unique_ptr<std::string> json_response); std::unique_ptr<std::string> json_response);
// Called when the data decoder service provides parsed JSON data for a server
// response.
void OnResponseJsonParsed(const std::set<std::string>& source_ids,
base::Optional<base::Value> json_data,
const base::Optional<std::string>& error);
// Adds the given results to the cache (if successful) and notifies clients.
void ProcessResults(
const std::set<std::string>& source_ids,
const std::map<std::string, mojom::AnnotateImageResultPtr>& results);
// Maps from source ID to previously-obtained annotation results. // Maps from source ID to previously-obtained annotation results.
// TODO(crbug.com/916420): periodically clear entries from this cache. // TODO(crbug.com/916420): periodically clear entries from this cache.
std::map<std::string, mojom::AnnotateImageResultPtr> cached_results_; std::map<std::string, mojom::AnnotateImageResultPtr> cached_results_;
...@@ -150,8 +171,13 @@ class Annotator : public mojom::Annotator { ...@@ -150,8 +171,13 @@ class Annotator : public mojom::Annotator {
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
service_manager::Connector* const connector_;
mojo::BindingSet<mojom::Annotator> bindings_; mojo::BindingSet<mojom::Annotator> bindings_;
// Should not be used directly; GetJsonParser() should be called instead.
data_decoder::mojom::JsonParserPtr json_parser_;
// A timer used to throttle HTTP request frequency. // A timer used to throttle HTTP request frequency.
base::RepeatingTimer http_request_timer_; base::RepeatingTimer http_request_timer_;
......
...@@ -29,7 +29,8 @@ ImageAnnotationService::ImageAnnotationService( ...@@ -29,7 +29,8 @@ ImageAnnotationService::ImageAnnotationService(
base::TimeDelta::FromMilliseconds(kThrottleMs.Get()), base::TimeDelta::FromMilliseconds(kThrottleMs.Get()),
kBatchSize.Get(), kBatchSize.Get(),
kMinOcrConfidence.Get(), kMinOcrConfidence.Get(),
shared_url_loader_factory) {} shared_url_loader_factory,
service_binding_.GetConnector()) {}
ImageAnnotationService::~ImageAnnotationService() = default; ImageAnnotationService::~ImageAnnotationService() = default;
......
...@@ -24,6 +24,7 @@ source_set("manifest") { ...@@ -24,6 +24,7 @@ source_set("manifest") {
public_deps = [ public_deps = [
"//base", "//base",
"//services/data_decoder/public/mojom:constants",
"//services/image_annotation/public/mojom", "//services/image_annotation/public/mojom",
"//services/service_manager/public/cpp", "//services/service_manager/public/cpp",
] ]
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "services/image_annotation/public/cpp/manifest.h" #include "services/image_annotation/public/cpp/manifest.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "services/data_decoder/public/mojom/constants.mojom.h"
#include "services/image_annotation/public/mojom/constants.mojom.h" #include "services/image_annotation/public/mojom/constants.mojom.h"
#include "services/image_annotation/public/mojom/image_annotation.mojom.h" #include "services/image_annotation/public/mojom/image_annotation.mojom.h"
#include "services/service_manager/public/cpp/manifest_builder.h" #include "services/service_manager/public/cpp/manifest_builder.h"
...@@ -19,6 +20,7 @@ const service_manager::Manifest& GetManifest() { ...@@ -19,6 +20,7 @@ const service_manager::Manifest& GetManifest() {
.ExposeCapability( .ExposeCapability(
mojom::kAnnotationCapability, mojom::kAnnotationCapability,
service_manager::Manifest::InterfaceList<mojom::Annotator>()) service_manager::Manifest::InterfaceList<mojom::Annotator>())
.RequireCapability(data_decoder::mojom::kServiceName, "json_parser")
.Build()}; .Build()};
return *manifest; return *manifest;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment