Commit 05d92917 authored by rbpotter's avatar rbpotter Committed by Commit Bot

Add validation for CDDs from extension and privet printers

To prevent JS console errors, ensure that all CDDs obtained from
extension and privet printers are sane. Do not need to check CDDs from
local printers, since they are generated by
SemanticCapsAndDefaultsToCdd.

Bug: None
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Idbbd9470cd966a47bb4b16472f05f30fe4990c4c
Reviewed-on: https://chromium-review.googlesource.com/679283
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#510151}
parent 5dfbdc57
......@@ -19,6 +19,7 @@
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/printing/pwg_raster_converter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
#include "components/cloud_devices/common/cloud_device_description.h"
#include "components/cloud_devices/common/printer_description.h"
#include "device/base/device_client.h"
......@@ -308,8 +309,11 @@ void ExtensionPrinterHandler::WrapGetCapabilityCallback(
const base::DictionaryValue& capability) {
std::unique_ptr<base::DictionaryValue> capabilities =
std::make_unique<base::DictionaryValue>();
if (!capability.empty()) // empty capability -> empty return dictionary
capabilities->SetPath({printing::kSettingCapabilities}, capability.Clone());
std::unique_ptr<base::DictionaryValue> cdd =
printing::ValidateCddForPrintPreview(capability);
// TODO (thestig): Remove call to Clone().
if (!cdd->empty()) // empty capability -> empty return dictionary
capabilities->SetPath({printing::kSettingCapabilities}, cdd->Clone());
callback.Run(std::move(capabilities));
}
......
......@@ -33,6 +33,17 @@ namespace printing {
const char kPrinter[] = "printer";
// Keys for a dictionary specifying a custom vendor capability. See
// settings/advanced_settings/advanced_settings_item.js in
// chrome/browser/resources/print_preview.
const char kOptionKey[] = "option";
const char kSelectCapKey[] = "select_cap";
const char kSelectString[] = "SELECT";
const char kTypeKey[] = "type";
// The dictionary key for the CDD item containing custom vendor capabilities.
const char kVendorCapabilityKey[] = "vendor_capability";
namespace {
// Returns a dictionary representing printer capabilities as CDD. Returns
......@@ -118,6 +129,39 @@ void PrintersToValues(const printing::PrinterList& printer_list,
}
}
template <typename Predicate>
base::Value GetFilteredList(const base::Value* list, Predicate pred) {
auto out_list = list->Clone();
base::EraseIf(out_list.GetList(), pred);
return out_list;
}
bool ValueIsNull(const base::Value& val) {
return val.is_none();
}
bool VendorCapabilityInvalid(const base::Value& val) {
if (!val.is_dict())
return true;
const base::Value* option_type =
val.FindPathOfType({kTypeKey}, base::Value::Type::STRING);
if (!option_type)
return true;
if (option_type->GetString() != kSelectString)
return false;
const base::Value* select_cap =
val.FindPathOfType({kSelectCapKey}, base::Value::Type::DICTIONARY);
if (!select_cap)
return true;
const base::Value* options_list =
select_cap->FindPathOfType({kOptionKey}, base::Value::Type::LIST);
if (!options_list || options_list->GetList().empty() ||
GetFilteredList(options_list, ValueIsNull).GetList().empty()) {
return true;
}
return false;
}
} // namespace
std::pair<std::string, std::string> GetPrinterNameAndDescription(
......@@ -181,4 +225,59 @@ void ConvertPrinterListForCallback(
callback.Run(printers);
done_callback.Run();
}
std::unique_ptr<base::DictionaryValue> ValidateCddForPrintPreview(
const base::DictionaryValue& cdd) {
auto out_final =
base::DictionaryValue::From(std::make_unique<base::Value>(cdd.Clone()));
const base::Value* caps = cdd.FindPath({kPrinter});
if (!caps || !caps->is_dict())
return out_final;
out_final->RemovePath({kPrinter});
auto out_caps = std::make_unique<base::DictionaryValue>();
for (const auto capability : caps->DictItems()) {
const auto& path = capability.first;
const base::Value* dict =
caps->FindPathOfType({path}, base::Value::Type::DICTIONARY);
const base::Value* list =
dict ? dict->FindPathOfType({kOptionKey}, base::Value::Type::LIST)
: caps->FindPathOfType({path}, base::Value::Type::LIST);
if (!list) {
out_caps->SetPath({path}, capability.second.Clone());
continue;
}
bool is_vendor_capability = path == kVendorCapabilityKey;
auto out_list = GetFilteredList(
list, is_vendor_capability ? VendorCapabilityInvalid : ValueIsNull);
if (out_list.GetList().empty()) // leave out empty lists.
continue;
if (is_vendor_capability) {
// Need to also filter the individual capability lists.
for (auto& vendor_option : out_list.GetList()) {
if (vendor_option.FindPathOfType({kTypeKey}, base::Value::Type::STRING)
->GetString() != kSelectString) {
continue;
}
base::Value* options_dict = vendor_option.FindPathOfType(
{kSelectCapKey}, base::Value::Type::DICTIONARY);
base::Value* options_list =
options_dict->FindPathOfType({kOptionKey}, base::Value::Type::LIST);
options_dict->SetPath({kOptionKey},
GetFilteredList(options_list, ValueIsNull));
}
}
if (dict) {
base::Value::DictStorage option_dict;
option_dict[kOptionKey] =
std::make_unique<base::Value>(std::move(out_list));
out_caps->SetPath({path}, base::Value(option_dict));
} else {
out_caps->SetPath({path}, std::move(out_list));
}
}
out_final->SetDictionary(kPrinter, std::move(out_caps));
return out_final;
}
} // namespace printing
......@@ -17,6 +17,15 @@ namespace printing {
struct PrinterBasicInfo;
// Printer capability setting keys.
extern const char kOptionKey[];
extern const char kPrinter[];
extern const char kTypeKey[];
extern const char kSelectCapKey[];
extern const char kSelectString[];
extern const char kTypeKey[];
extern const char kVendorCapabilityKey[];
// Extracts the printer display name and description from the
// appropriate fields in |printer| for the platform.
std::pair<std::string, std::string> GetPrinterNameAndDescription(
......@@ -35,6 +44,12 @@ void ConvertPrinterListForCallback(
const PrinterHandler::AddedPrintersCallback& callback,
const PrinterHandler::GetPrintersDoneCallback& done_callback,
const printing::PrinterList& printer_list);
// Returns a unique_ptr to a sanitized version of |cdd| to prevent possible JS
// errors in Print Preview. Will remove null items from lists or options lists
// and remove any lists/options that are empty or only contain null values.
std::unique_ptr<base::DictionaryValue> ValidateCddForPrintPreview(
const base::DictionaryValue& cdd);
} // namespace printing
#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_CAPABILITIES_H_
......@@ -19,6 +19,7 @@
#include "chrome/browser/printing/cloud_print/privet_constants.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
#include "chrome/common/chrome_switches.h"
#include "components/signin/core/browser/signin_manager.h"
#include "ui/gfx/geometry/size.h"
......@@ -190,14 +191,14 @@ void PrivetPrinterHandler::OnGotCapabilities(
std::unique_ptr<base::DictionaryValue> printer_info =
std::make_unique<base::DictionaryValue>();
FillPrinterDescription(name, *description, true, printer_info.get());
std::unique_ptr<base::DictionaryValue> printer_info_and_caps =
std::make_unique<base::DictionaryValue>();
printer_info_and_caps->SetDictionary("printer", std::move(printer_info));
base::DictionaryValue printer_info_and_caps;
printer_info_and_caps.SetDictionary(printing::kPrinter,
std::move(printer_info));
std::unique_ptr<base::DictionaryValue> capabilities_copy =
capabilities->CreateDeepCopy();
printer_info_and_caps->SetDictionary("capabilities",
std::move(capabilities_copy));
callback.Run(std::move(printer_info_and_caps));
printer_info_and_caps.SetDictionary(printing::kSettingCapabilities,
std::move(capabilities_copy));
callback.Run(printing::ValidateCddForPrintPreview(printer_info_and_caps));
privet_capabilities_operation_.reset();
}
......
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