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..0aefd5c29c2b0eb960416c1a107f63a1b66b7be1 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,27 @@ 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); |
not at google - send to devlin
2014/03/21 15:27:20
if it.value() isn't a DictionaryValue then |dictio
binjin
2014/03/24 14:35:09
I think the type of it.value() have already been c
not at google - send to devlin
2014/03/25 14:38:05
ah, right.
|
- 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 valid |
+ // regular expression to a schema. |
+ if (it.key() == schema::kPatternProperties) { |
+ it.value().GetAsDictionary(&dictionary_value); |
not at google - send to devlin
2014/03/21 15:27:20
... though this would have the same bug.
binjin
2014/03/24 14:35:09
as noted above
|
+ for (base::DictionaryValue::Iterator iter(*dictionary_value); |
+ !iter.IsAtEnd(); iter.Advance()) { |
+ if (!iter.value().GetAsDictionary(&dictionary_value)) { |
not at google - send to devlin
2014/03/21 15:27:20
add a comment why you're not validating iter.key()
binjin
2014/03/24 14:35:09
Done.
|
+ *error = "patternProperties must be a dictionary"; |
return false; |
} |
if (!IsValidSchema(dictionary_value, options, error)) { |
@@ -308,6 +331,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 +576,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()); |
@@ -574,17 +598,54 @@ void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, |
} |
} |
+ // Allowing any additional items will ignore pattern properties as well. |
not at google - send to devlin
2014/03/21 15:27:20
I don't understand this comment, you're not ignori
binjin
2014/03/24 14:35:09
Removed. In previous changeset there is a "if(allo
|
const base::DictionaryValue* additional_properties_schema = NULL; |
- if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) |
- return; |
+ bool allow_any_additional_properties = |
+ SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema); |
+ |
+ 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()))); |
not at google - send to devlin
2014/03/21 15:27:20
hm, why not continue after this? what is the point
binjin
2014/03/24 14:35:09
Done.
|
+ } |
+ 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 additional properties. |
+ // 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 (pattern_properties_pattern[index]->ok() && |
not at google - send to devlin
2014/03/21 15:27:20
(in which case you wouldn't need this check)
binjin
2014/03/24 14:35:09
Done.
|
+ 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 +768,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, |