| Index: components/json_schema/json_schema_validator.cc
|
| diff --git a/components/json_schema/json_schema_validator.cc b/components/json_schema/json_schema_validator.cc
|
| index 18558d0e111213bd789ac96633b8bc43afaaa53f..1899ad6798d235ab38a464c20cd51ec436b81b42 100644
|
| --- a/components/json_schema/json_schema_validator.cc
|
| +++ b/components/json_schema/json_schema_validator.cc
|
| @@ -7,13 +7,17 @@
|
| #include <algorithm>
|
| #include <cfloat>
|
| #include <cmath>
|
| +#include <vector>
|
|
|
| #include "base/json/json_reader.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_vector.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "base/values.h"
|
| #include "components/json_schema/json_schema_constants.h"
|
| +#include "third_party/re2/re2/re2.h"
|
|
|
| namespace schema = json_schema_constants;
|
|
|
| @@ -86,6 +90,8 @@ bool IsValidSchema(const base::DictionaryValue* dict,
|
| { schema::kMinLength, base::Value::TYPE_INTEGER },
|
| { schema::kMinimum, base::Value::TYPE_DOUBLE },
|
| { schema::kOptional, base::Value::TYPE_BOOLEAN },
|
| + { schema::kPattern, base::Value::TYPE_STRING },
|
| + { schema::kPatternProperties, base::Value::TYPE_DICTIONARY },
|
| { schema::kProperties, base::Value::TYPE_DICTIONARY },
|
| { schema::kTitle, base::Value::TYPE_STRING },
|
| };
|
| @@ -186,10 +192,29 @@ bool IsValidSchema(const base::DictionaryValue* dict,
|
| // Validate the "properties" attribute. Each entry maps a key to a schema.
|
| if (it.key() == schema::kProperties) {
|
| it.value().GetAsDictionary(&dictionary_value);
|
| - for (base::DictionaryValue::Iterator it(*dictionary_value);
|
| - !it.IsAtEnd(); it.Advance()) {
|
| - if (!it.value().GetAsDictionary(&dictionary_value)) {
|
| - *error = "Invalid value for properties attribute";
|
| + for (base::DictionaryValue::Iterator iter(*dictionary_value);
|
| + !iter.IsAtEnd(); iter.Advance()) {
|
| + if (!iter.value().GetAsDictionary(&dictionary_value)) {
|
| + *error = "properties must be a dictionary";
|
| + return false;
|
| + }
|
| + if (!IsValidSchema(dictionary_value, options, error)) {
|
| + DCHECK(!error->empty());
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Validate the "patternProperties" attribute. Each entry maps a regular
|
| + // expression to a schema. The validity of the regular expression expression
|
| + // won't be checked here for performance reasons. Instead, invalid regular
|
| + // expressions will be caught as validation errors in Validate().
|
| + if (it.key() == schema::kPatternProperties) {
|
| + it.value().GetAsDictionary(&dictionary_value);
|
| + for (base::DictionaryValue::Iterator iter(*dictionary_value);
|
| + !iter.IsAtEnd(); iter.Advance()) {
|
| + if (!iter.value().GetAsDictionary(&dictionary_value)) {
|
| + *error = "patternProperties must be a dictionary";
|
| return false;
|
| }
|
| if (!IsValidSchema(dictionary_value, options, error)) {
|
| @@ -308,6 +333,8 @@ const char JSONSchemaValidator::kInvalidType[] =
|
| "Expected '*' but got '*'.";
|
| const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] =
|
| "Expected 'integer' but got 'number', consider using Math.round().";
|
| +const char JSONSchemaValidator::kInvalidRegex[] =
|
| + "Regular expression /*/ is invalid: *";
|
|
|
|
|
| // static
|
| @@ -551,8 +578,7 @@ void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance,
|
| const base::DictionaryValue* schema,
|
| const std::string& path) {
|
| const base::DictionaryValue* properties = NULL;
|
| - schema->GetDictionary(schema::kProperties, &properties);
|
| - if (properties) {
|
| + if (schema->GetDictionary(schema::kProperties, &properties)) {
|
| for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd();
|
| it.Advance()) {
|
| std::string prop_path = path.empty() ? it.key() : (path + "." + it.key());
|
| @@ -575,16 +601,52 @@ void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance,
|
| }
|
|
|
| const base::DictionaryValue* additional_properties_schema = NULL;
|
| - if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema))
|
| - return;
|
| + bool allow_any_additional_properties =
|
| + SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema);
|
|
|
| - // Validate additional properties.
|
| + const base::DictionaryValue* pattern_properties = NULL;
|
| + ScopedVector<re2::RE2> pattern_properties_pattern;
|
| + std::vector<const base::DictionaryValue*> pattern_properties_schema;
|
| +
|
| + if (schema->GetDictionary(schema::kPatternProperties, &pattern_properties)) {
|
| + for (base::DictionaryValue::Iterator it(*pattern_properties); !it.IsAtEnd();
|
| + it.Advance()) {
|
| + re2::RE2* prop_pattern = new re2::RE2(it.key());
|
| + if (!prop_pattern->ok()) {
|
| + LOG(WARNING) << "Regular expression /" << it.key()
|
| + << "/ is invalid: " << prop_pattern->error() << ".";
|
| + errors_.push_back(
|
| + Error(path,
|
| + FormatErrorMessage(
|
| + kInvalidRegex, it.key(), prop_pattern->error())));
|
| + continue;
|
| + }
|
| + const base::DictionaryValue* prop_schema = NULL;
|
| + CHECK(it.value().GetAsDictionary(&prop_schema));
|
| + pattern_properties_pattern.push_back(prop_pattern);
|
| + pattern_properties_schema.push_back(prop_schema);
|
| + }
|
| + }
|
| +
|
| + // Validate pattern properties and additional properties.
|
| for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd();
|
| it.Advance()) {
|
| - if (properties && properties->HasKey(it.key()))
|
| + std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
|
| +
|
| + bool found_matching_pattern = false;
|
| + for (size_t index = 0; index < pattern_properties_pattern.size(); ++index) {
|
| + if (re2::RE2::PartialMatch(it.key(),
|
| + *pattern_properties_pattern[index])) {
|
| + found_matching_pattern = true;
|
| + Validate(&it.value(), pattern_properties_schema[index], prop_path);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (found_matching_pattern || allow_any_additional_properties ||
|
| + (properties && properties->HasKey(it.key())))
|
| continue;
|
|
|
| - std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
|
| if (!additional_properties_schema) {
|
| errors_.push_back(Error(prop_path, kUnexpectedProperty));
|
| } else {
|
| @@ -707,7 +769,20 @@ void JSONSchemaValidator::ValidateString(const base::Value* instance,
|
| }
|
| }
|
|
|
| - CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported.";
|
| + std::string pattern;
|
| + if (schema->GetString(schema::kPattern, &pattern)) {
|
| + re2::RE2 compiled_regex(pattern);
|
| + if (!compiled_regex.ok()) {
|
| + LOG(WARNING) << "Regular expression /" << pattern
|
| + << "/ is invalid: " << compiled_regex.error() << ".";
|
| + errors_.push_back(Error(
|
| + path,
|
| + FormatErrorMessage(kInvalidRegex, pattern, compiled_regex.error())));
|
| + } else if (!re2::RE2::PartialMatch(value, compiled_regex)) {
|
| + errors_.push_back(
|
| + Error(path, FormatErrorMessage(kStringPattern, pattern)));
|
| + }
|
| + }
|
| }
|
|
|
| void JSONSchemaValidator::ValidateNumber(const base::Value* instance,
|
|
|