summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorAlexander Hendrich <hendrich@chromium.org>2018-08-29 19:54:17 +0900
committerQijiang Fan <fqj@google.com>2020-06-05 10:24:36 +0900
commit28e59112e925c1f34f13ab47abf98ea9ba7236a1 (patch)
treebcf3211dc483290e8814fa79da6b21802e692e95 /components
parent58d051bcdb3ecfa0b693dbd9a02dbe0015493a66 (diff)
downloadlibchrome-28e59112e925c1f34f13ab47abf98ea9ba7236a1.tar.gz
Clean-up schema validator features for schema/value validation
This Cl is part of a larger clean-up operation and removes unnecessary features and data types, which are not supported by both schema and value validation. This CL also adds documentation about the supported subset of JSON schema features. Bug: 873641, 856901 Change-Id: I8d464ab7b6404bde5b0f1372cb04a92a0e07080f Reviewed-on: https://chromium-review.googlesource.com/1183186 Commit-Queue: Alexander Hendrich <hendrich@chromium.org> Reviewed-by: Lutz Justen <ljusten@chromium.org> Cr-Commit-Position: refs/heads/master@{#587068} CrOS-Libchrome-Original-Commit: d538aa8b13fdccf6c8653538c13cae7899a8ec09
Diffstat (limited to 'components')
-rw-r--r--components/policy/core/common/json_schema_constants.cc9
-rw-r--r--components/policy/core/common/json_schema_constants.h9
-rw-r--r--components/policy/core/common/schema.cc594
-rw-r--r--components/policy/core/common/schema.h34
-rw-r--r--components/policy/core/common/schema_map_unittest.cc56
-rw-r--r--components/policy/core/common/schema_registry_unittest.cc41
-rw-r--r--components/policy/core/common/schema_unittest.cc264
7 files changed, 518 insertions, 489 deletions
diff --git a/components/policy/core/common/json_schema_constants.cc b/components/policy/core/common/json_schema_constants.cc
index 1a6d1ac0fd..fdfc811426 100644
--- a/components/policy/core/common/json_schema_constants.cc
+++ b/components/policy/core/common/json_schema_constants.cc
@@ -7,31 +7,22 @@
namespace json_schema_constants {
const char kAdditionalProperties[] = "additionalProperties";
-const char kAny[] = "any";
const char kArray[] = "array";
const char kBoolean[] = "boolean";
-const char kChoices[] = "choices";
const char kDescription[] = "description";
const char kEnum[] = "enum";
const char kId[] = "id";
const char kInteger[] = "integer";
const char kItems[] = "items";
const char kMaximum[] = "maximum";
-const char kMaxItems[] = "maxItems";
-const char kMaxLength[] = "maxLength";
const char kMinimum[] = "minimum";
-const char kMinItems[] = "minItems";
-const char kMinLength[] = "minLength";
-const char kNull[] = "null";
const char kNumber[] = "number";
const char kObject[] = "object";
-const char kOptional[] = "optional";
const char kPattern[] = "pattern";
const char kPatternProperties[] = "patternProperties";
const char kProperties[] = "properties";
const char kRef[] = "$ref";
const char kRequired[] = "required";
-const char kSchema[] = "$schema";
const char kString[] = "string";
const char kTitle[] = "title";
const char kType[] = "type";
diff --git a/components/policy/core/common/json_schema_constants.h b/components/policy/core/common/json_schema_constants.h
index 66b7941d46..42d03986c1 100644
--- a/components/policy/core/common/json_schema_constants.h
+++ b/components/policy/core/common/json_schema_constants.h
@@ -9,31 +9,22 @@
namespace json_schema_constants {
extern const char kAdditionalProperties[];
-extern const char kAny[];
extern const char kArray[];
extern const char kBoolean[];
-extern const char kChoices[];
extern const char kDescription[];
extern const char kEnum[];
extern const char kId[];
extern const char kInteger[];
extern const char kItems[];
extern const char kMaximum[];
-extern const char kMaxItems[];
-extern const char kMaxLength[];
extern const char kMinimum[];
-extern const char kMinItems[];
-extern const char kMinLength[];
-extern const char kNull[];
extern const char kNumber[];
extern const char kObject[];
-extern const char kOptional[];
extern const char kPattern[];
extern const char kPatternProperties[];
extern const char kProperties[];
extern const char kRef[];
extern const char kRequired[];
-extern const char kSchema[];
extern const char kString[];
extern const char kTitle[];
extern const char kType[];
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
index be475c4ff2..e6c13f680a 100644
--- a/components/policy/core/common/schema.cc
+++ b/components/policy/core/common/schema.cc
@@ -83,28 +83,139 @@ constexpr char kSensitiveValueMask[] = "********";
// pointer.
const int kInvalid = -1;
-bool SchemaTypeToValueType(const std::string& type_string,
- base::Value::Type* type) {
- // Note: "any" is not an accepted type.
- static const struct {
- const char* schema_type;
- base::Value::Type value_type;
- } kSchemaToValueTypeMap[] = {
- { schema::kArray, base::Value::Type::LIST },
- { schema::kBoolean, base::Value::Type::BOOLEAN },
- { schema::kInteger, base::Value::Type::INTEGER },
- { schema::kNull, base::Value::Type::NONE },
- { schema::kNumber, base::Value::Type::DOUBLE },
- { schema::kObject, base::Value::Type::DICTIONARY },
- { schema::kString, base::Value::Type::STRING },
- };
- for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
- if (kSchemaToValueTypeMap[i].schema_type == type_string) {
- *type = kSchemaToValueTypeMap[i].value_type;
- return true;
- }
- }
- return false;
+// Maps a schema key to the corresponding base::Value::Type
+struct SchemaKeyToValueType {
+ const char* key;
+ base::Value::Type type;
+};
+
+// Allowed types and their base::Value::Type equivalent. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kSchemaTypesToValueTypes[] = {
+ {schema::kArray, base::Value::Type::LIST},
+ {schema::kBoolean, base::Value::Type::BOOLEAN},
+ {schema::kInteger, base::Value::Type::INTEGER},
+ {schema::kNumber, base::Value::Type::DOUBLE},
+ {schema::kObject, base::Value::Type::DICTIONARY},
+ {schema::kString, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kSchemaTypesToValueTypesEnd =
+ kSchemaTypesToValueTypes + base::size(kSchemaTypesToValueTypes);
+
+// Allowed attributes and types for type 'array'. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForArray[] = {
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kId, base::Value::Type::STRING},
+ {schema::kItems, base::Value::Type::DICTIONARY},
+ {schema::kTitle, base::Value::Type::STRING},
+ {schema::kType, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForArrayEnd =
+ kAttributesAndTypesForArray + base::size(kAttributesAndTypesForArray);
+
+// Allowed attributes and types for type 'boolean'. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForBoolean[] = {
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kId, base::Value::Type::STRING},
+ {schema::kTitle, base::Value::Type::STRING},
+ {schema::kType, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForBooleanEnd =
+ kAttributesAndTypesForBoolean + base::size(kAttributesAndTypesForBoolean);
+
+// Allowed attributes and types for type 'integer'. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForInteger[] = {
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kEnum, base::Value::Type::LIST},
+ {schema::kId, base::Value::Type::STRING},
+ {schema::kMaximum, base::Value::Type::DOUBLE},
+ {schema::kMinimum, base::Value::Type::DOUBLE},
+ {schema::kTitle, base::Value::Type::STRING},
+ {schema::kType, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForIntegerEnd =
+ kAttributesAndTypesForInteger + base::size(kAttributesAndTypesForInteger);
+
+// Allowed attributes and types for type 'number'. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForNumber[] = {
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kId, base::Value::Type::STRING},
+ {schema::kTitle, base::Value::Type::STRING},
+ {schema::kType, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForNumberEnd =
+ kAttributesAndTypesForNumber + base::size(kAttributesAndTypesForNumber);
+
+// Allowed attributes and types for type 'object'. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForObject[] = {
+ {schema::kAdditionalProperties, base::Value::Type::DICTIONARY},
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kId, base::Value::Type::STRING},
+ {schema::kPatternProperties, base::Value::Type::DICTIONARY},
+ {schema::kProperties, base::Value::Type::DICTIONARY},
+ {schema::kRequired, base::Value::Type::LIST},
+ {schema::kTitle, base::Value::Type::STRING},
+ {schema::kType, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForObjectEnd =
+ kAttributesAndTypesForObject + base::size(kAttributesAndTypesForObject);
+
+// Allowed attributes and types for $ref. These are ordered alphabetically to
+// perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForRef[] = {
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kRef, base::Value::Type::STRING},
+ {schema::kTitle, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForRefEnd =
+ kAttributesAndTypesForRef + base::size(kAttributesAndTypesForRef);
+
+// Allowed attributes and types for type 'string'. These are ordered
+// alphabetically to perform binary search.
+const SchemaKeyToValueType kAttributesAndTypesForString[] = {
+ {schema::kDescription, base::Value::Type::STRING},
+ {schema::kEnum, base::Value::Type::LIST},
+ {schema::kId, base::Value::Type::STRING},
+ {schema::kPattern, base::Value::Type::STRING},
+ {schema::kTitle, base::Value::Type::STRING},
+ {schema::kType, base::Value::Type::STRING},
+};
+const SchemaKeyToValueType* kAttributesAndTypesForStringEnd =
+ kAttributesAndTypesForString + base::size(kAttributesAndTypesForString);
+
+// Helper for std::lower_bound.
+bool CompareToString(const SchemaKeyToValueType& entry,
+ const std::string& key) {
+ return entry.key < key;
+}
+
+// Returns true if a SchemaKeyToValueType with key==|schema_key| can be found in
+// the array represented by |begin| and |end|. If so, |value_type| will be set
+// to the SchemaKeyToValueType value type.
+bool MapSchemaKeyToValueType(const std::string& schema_key,
+ const SchemaKeyToValueType* begin,
+ const SchemaKeyToValueType* end,
+ base::Value::Type* value_type) {
+ const SchemaKeyToValueType* entry =
+ std::lower_bound(begin, end, schema_key, CompareToString);
+ if (entry == end || entry->key != schema_key)
+ return false;
+ if (value_type)
+ *value_type = entry->type;
+ return true;
+}
+
+// Shorthand method for |SchemaTypeToValueType()| with
+// |kSchemaTypesToValueTypes|.
+bool SchemaTypeToValueType(const std::string& schema_type,
+ base::Value::Type* value_type) {
+ return MapSchemaKeyToValueType(schema_type, kSchemaTypesToValueTypes,
+ kSchemaTypesToValueTypesEnd, value_type);
}
bool StrategyAllowInvalidOnTopLevel(SchemaOnErrorStrategy strategy) {
@@ -155,278 +266,226 @@ void AddDictKeyPrefixToPath(const std::string& key, std::string* path) {
}
}
-bool IsValidType(const std::string& type) {
- static const char* kValidTypes[] = {
- schema::kAny, schema::kArray, schema::kBoolean, schema::kInteger,
- schema::kNull, schema::kNumber, schema::kObject, schema::kString,
- };
- const char** end = kValidTypes + base::size(kValidTypes);
- return std::find(kValidTypes, end, type) != end;
+bool IgnoreUnknownAttributes(int options) {
+ return (options & kSchemaOptionsIgnoreUnknownAttributes);
}
-// Maps a schema attribute name to its expected type.
-struct ExpectedType {
- const char* key;
- base::Value::Type type;
-};
-
-// Helper for std::lower_bound.
-bool CompareToString(const ExpectedType& entry, const std::string& key) {
- return entry.key < key;
+// Check that the value's type and the expected type are equal. We also allow
+// integers when expecting doubles.
+bool CheckType(const base::Value* value, base::Value::Type expected_type) {
+ return value->type() == expected_type ||
+ (value->is_int() && expected_type == base::Value::Type::DOUBLE);
}
-// If |value| is a dictionary, returns the "name" attribute of |value| or NULL
-// if |value| does not contain a "name" attribute. Otherwise, returns |value|.
-const base::Value* ExtractNameFromDictionary(const base::Value* value) {
- const base::DictionaryValue* value_dict = nullptr;
- const base::Value* name_value = nullptr;
- if (value->GetAsDictionary(&value_dict)) {
- value_dict->Get("name", &name_value);
- return name_value;
- }
- return value;
+// Returns true if |type| is supported as schema's 'type' value.
+bool IsValidType(const std::string& type) {
+ return MapSchemaKeyToValueType(type, kSchemaTypesToValueTypes,
+ kSchemaTypesToValueTypesEnd, nullptr);
}
-bool IsValidSchema(const base::DictionaryValue* dict,
- int options,
- std::string* error) {
- // This array must be sorted, so that std::lower_bound can perform a
- // binary search.
- static const ExpectedType kExpectedTypes[] = {
- // Note: kRef == "$ref", kSchema == "$schema"
- {schema::kRef, base::Value::Type::STRING},
- {schema::kSchema, base::Value::Type::STRING},
-
- {schema::kAdditionalProperties, base::Value::Type::DICTIONARY},
- {schema::kChoices, base::Value::Type::LIST},
- {schema::kDescription, base::Value::Type::STRING},
- {schema::kEnum, base::Value::Type::LIST},
- {schema::kId, base::Value::Type::STRING},
- {schema::kMaxItems, base::Value::Type::INTEGER},
- {schema::kMaxLength, base::Value::Type::INTEGER},
- {schema::kMaximum, base::Value::Type::DOUBLE},
- {schema::kMinItems, base::Value::Type::INTEGER},
- {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::kRequired, base::Value::Type::LIST},
- {schema::kTitle, base::Value::Type::STRING},
- };
-
- bool has_type_or_ref = false;
- const base::ListValue* list_value = nullptr;
- const base::DictionaryValue* dictionary_value = nullptr;
- std::string string_value;
-
- const base::ListValue* required_properties_value = nullptr;
- const base::DictionaryValue* properties_value = nullptr;
-
- for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
- // Validate the "type" attribute, which may be a string or a list.
- if (it.key() == schema::kType) {
- switch (it.value().type()) {
- case base::Value::Type::STRING:
- it.value().GetAsString(&string_value);
- if (!IsValidType(string_value)) {
- *error = "Invalid value for type attribute";
- return false;
- }
- break;
- case base::Value::Type::LIST:
- it.value().GetAsList(&list_value);
- for (size_t i = 0; i < list_value->GetSize(); ++i) {
- if (!list_value->GetString(i, &string_value) ||
- !IsValidType(string_value)) {
- *error = "Invalid value for type attribute";
- return false;
- }
- }
- break;
- default:
- *error = "Invalid value for type attribute";
- return false;
- }
- has_type_or_ref = true;
- continue;
- }
+// Validate that |dict| only contains attributes that are allowed for the
+// corresponding value of 'type'. Also ensure that all of those attributes are
+// of the expected type. |options| can be used to ignore unknown attributes.
+bool ValidateAttributesAndTypes(const base::DictionaryValue* dict,
+ const std::string& type,
+ int options,
+ std::string* error) {
+ const SchemaKeyToValueType* begin = nullptr;
+ const SchemaKeyToValueType* end = nullptr;
+ if (type == schema::kArray) {
+ begin = kAttributesAndTypesForArray;
+ end = kAttributesAndTypesForArrayEnd;
+ } else if (type == schema::kBoolean) {
+ begin = kAttributesAndTypesForBoolean;
+ end = kAttributesAndTypesForBooleanEnd;
+ } else if (type == schema::kInteger) {
+ begin = kAttributesAndTypesForInteger;
+ end = kAttributesAndTypesForIntegerEnd;
+ } else if (type == schema::kNumber) {
+ begin = kAttributesAndTypesForNumber;
+ end = kAttributesAndTypesForNumberEnd;
+ } else if (type == schema::kObject) {
+ begin = kAttributesAndTypesForObject;
+ end = kAttributesAndTypesForObjectEnd;
+ } else if (type == schema::kRef) {
+ begin = kAttributesAndTypesForRef;
+ end = kAttributesAndTypesForRefEnd;
+ } else if (type == schema::kString) {
+ begin = kAttributesAndTypesForString;
+ end = kAttributesAndTypesForStringEnd;
+ } else {
+ NOTREACHED() << "Type should be a valid schema type or '$ref'.";
+ }
- // Validate the "items" attribute, which is a schema or a list of schemas.
- if (it.key() == schema::kItems) {
- if (it.value().GetAsDictionary(&dictionary_value)) {
- if (!IsValidSchema(dictionary_value, options, error)) {
- DCHECK(!error->empty());
- return false;
- }
- } else if (it.value().GetAsList(&list_value)) {
- for (size_t i = 0; i < list_value->GetSize(); ++i) {
- if (!list_value->GetDictionary(i, &dictionary_value)) {
- *error = base::StringPrintf(
- "Invalid entry in items attribute at index %d",
- static_cast<int>(i));
- return false;
- }
- if (!IsValidSchema(dictionary_value, options, error)) {
- DCHECK(!error->empty());
- return false;
- }
- }
- } else {
- *error = "Invalid value for items attribute";
+ base::Value::Type expected_type = base::Value::Type::NONE;
+ for (const auto& it : dict->DictItems()) {
+ if (MapSchemaKeyToValueType(it.first, begin, end, &expected_type)) {
+ if (!CheckType(&it.second, expected_type)) {
+ *error = base::StringPrintf("Invalid type for attribute '%s'",
+ it.first.c_str());
return false;
}
- continue;
+ } else if (!IgnoreUnknownAttributes(options)) {
+ *error = base::StringPrintf("Unknown attribute '%s'", it.first.c_str());
+ return false;
}
+ }
+ return true;
+}
- // All the other attributes have a single valid type.
- const ExpectedType* end = kExpectedTypes + base::size(kExpectedTypes);
- const ExpectedType* entry =
- std::lower_bound(kExpectedTypes, end, it.key(), CompareToString);
- if (entry == end || entry->key != it.key()) {
- if (options & Schema::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES)
- continue;
- *error = base::StringPrintf("Invalid attribute %s", it.key().c_str());
+// Validates that |enum_value| is a list and its items are all of type |type|.
+bool ValidateEnum(const base::Value* enum_value,
+ const std::string& type,
+ std::string* error) {
+ if (enum_value->type() != base::Value::Type::LIST ||
+ enum_value->GetList().empty()) {
+ *error = "Attribute 'enum' must be a non-empty list.";
+ return false;
+ }
+ base::Value::Type expected_item_type = base::Value::Type::NONE;
+ MapSchemaKeyToValueType(type, kSchemaTypesToValueTypes,
+ kSchemaTypesToValueTypesEnd, &expected_item_type);
+ for (const base::Value& item : enum_value->GetList()) {
+ if (item.type() != expected_item_type) {
+ *error = base::StringPrintf(
+ "Attribute 'enum' for type '%s' contains items with invalid types",
+ type.c_str());
return false;
}
+ }
+ return true;
+}
- // Integer can be converted to double.
- if (!(it.value().type() == entry->type ||
- (it.value().is_int() && entry->type == base::Value::Type::DOUBLE))) {
- *error = base::StringPrintf("Invalid value for %s attribute",
- it.key().c_str());
+// Forward declaration (used in ValidateProperties).
+bool IsValidSchema(const base::DictionaryValue* dict,
+ int options,
+ std::string* error);
+
+// Validates that the values in the |properties| dict are valid schemas.
+bool ValidateProperties(const base::Value* properties,
+ int options,
+ std::string* error) {
+ for (const auto& dict_it : properties->DictItems()) {
+ if (dict_it.second.type() != base::Value::Type::DICTIONARY) {
+ *error = base::StringPrintf("Schema for property '%s' must be a dict.",
+ dict_it.first.c_str());
return false;
}
+ const base::DictionaryValue* property_schema_dict;
+ dict_it.second.GetAsDictionary(&property_schema_dict);
+ if (!IsValidSchema(property_schema_dict, options, error))
+ return false;
+ }
+ return true;
+}
- // base::Value::Type::INTEGER attributes must be >= 0.
- // This applies to "minItems", "maxItems", "minLength" and "maxLength".
- if (it.value().is_int()) {
- int integer_value;
- it.value().GetAsInteger(&integer_value);
- if (integer_value < 0) {
- *error = base::StringPrintf("Value of %s must be >= 0, got %d",
- it.key().c_str(), integer_value);
- return false;
- }
- }
+// Checks whether the passed dict is a valid schema. See
+// |kAllowedAttributesAndTypes| for a list of supported types, supported
+// attributes and their expected types. Values for 'minimum' and 'maximum' for
+// type 'integer' can be of type int or double. Referenced IDs ($ref) are not
+// checked for existence and IDs are not checked for duplicates. The 'pattern'
+// attribute and keys for 'patternProperties' are not checked for valid regulax
+// expression syntax. Invalid regular expressions will cause a value validation
+// error.
+bool IsValidSchema(const base::DictionaryValue* dict,
+ int options,
+ std::string* error) {
+ // Validate '$ref'.
+ const base::Value* ref_id = dict->FindKey(schema::kRef);
+ if (ref_id)
+ return ValidateAttributesAndTypes(dict, schema::kRef, options, error);
+
+ // Validate 'type'.
+ const base::Value* type = dict->FindKey(schema::kType);
+ if (!type) {
+ *error = "Each schema must have a 'type' or '$ref'.";
+ return false;
+ }
+ if (type->type() != base::Value::Type::STRING) {
+ *error = "Attribute 'type' must be a string.";
+ return false;
+ }
+ const std::string type_string = type->GetString();
+ if (!IsValidType(type_string)) {
+ *error = base::StringPrintf("Unknown type '%s'.", type_string.c_str());
+ return false;
+ }
- // Validate the "properties" attribute. Each entry maps a key to a schema.
- if (it.key() == schema::kProperties) {
- it.value().GetAsDictionary(&properties_value);
- for (base::DictionaryValue::Iterator iter(*properties_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 attributes and expected types.
+ if (!ValidateAttributesAndTypes(dict, type_string, options, error))
+ 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)) {
- DCHECK(!error->empty());
- return false;
- }
- }
- }
+ // Validate 'enum' attribute.
+ if (type_string == schema::kString || type_string == schema::kInteger) {
+ const base::Value* enum_list = dict->FindKey(schema::kEnum);
+ if (enum_list && !ValidateEnum(enum_list, type_string, error))
+ return false;
+ }
- // Validate "additionalProperties" attribute, which is a schema.
- if (it.key() == schema::kAdditionalProperties) {
- it.value().GetAsDictionary(&dictionary_value);
- if (!IsValidSchema(dictionary_value, options, error)) {
- DCHECK(!error->empty());
+ if (type_string == schema::kInteger) {
+ // Validate 'minimum' > 'maximum'.
+ const base::Value* minimum_value = dict->FindKey(schema::kMinimum);
+ const base::Value* maximum_value = dict->FindKey(schema::kMaximum);
+ if (minimum_value && maximum_value) {
+ double minimum = minimum_value->is_int() ? minimum_value->GetInt()
+ : minimum_value->GetDouble();
+ double maximum = maximum_value->is_int() ? maximum_value->GetInt()
+ : maximum_value->GetDouble();
+ if (minimum > maximum) {
+ *error = base::StringPrintf("Invalid range specified [%f;%f].", minimum,
+ maximum);
return false;
}
}
+ } else if (type_string == schema::kArray) {
+ // Validate type 'array'.
+ const base::Value* items_value = dict->FindKey(schema::kItems);
+ if (!items_value) {
+ *error = "Schema of type 'array' must have a schema in 'items'.";
+ return false;
+ }
+ const base::DictionaryValue* items_dict;
+ items_value->GetAsDictionary(&items_dict);
+ if (!IsValidSchema(items_dict, options, error))
+ return false;
+ } else if (type_string == schema::kObject) {
+ // Validate type 'object'.
+ const base::Value* properties = dict->FindKey(schema::kProperties);
+ if (properties && !ValidateProperties(properties, options, error))
+ return false;
- // Validate "required" attribute.
- if (it.key() == schema::kRequired) {
- it.value().GetAsList(&required_properties_value);
- for (const base::Value& value : *required_properties_value) {
- if (value.type() != base::Value::Type::STRING) {
- *error = "Invalid value in 'required' attribute";
- return false;
- }
- }
+ const base::Value* pattern_properties =
+ dict->FindKey(schema::kPatternProperties);
+ if (pattern_properties &&
+ !ValidateProperties(pattern_properties, options, error)) {
+ return false;
}
- // Validate the values contained in an "enum" attribute.
- if (it.key() == schema::kEnum) {
- it.value().GetAsList(&list_value);
- for (size_t i = 0; i < list_value->GetSize(); ++i) {
- const base::Value* value = nullptr;
- list_value->Get(i, &value);
- // Sometimes the enum declaration is a dictionary with the enum value
- // under "name".
- value = ExtractNameFromDictionary(value);
- if (!value) {
- *error = "Invalid value in enum attribute";
- return false;
- }
- switch (value->type()) {
- case base::Value::Type::NONE:
- case base::Value::Type::BOOLEAN:
- case base::Value::Type::INTEGER:
- case base::Value::Type::DOUBLE:
- case base::Value::Type::STRING:
- break;
- default:
- *error = "Invalid value in enum attribute";
- return false;
- }
- }
+ const base::Value* additional_properties =
+ dict->FindKey(schema::kAdditionalProperties);
+ if (additional_properties) {
+ const base::DictionaryValue* additional_properties_dict;
+ additional_properties->GetAsDictionary(&additional_properties_dict);
+ if (!IsValidSchema(additional_properties_dict, options, error))
+ return false;
}
- // Validate the schemas contained in a "choices" attribute.
- if (it.key() == schema::kChoices) {
- it.value().GetAsList(&list_value);
- for (size_t i = 0; i < list_value->GetSize(); ++i) {
- if (!list_value->GetDictionary(i, &dictionary_value)) {
- *error = "Invalid choices attribute";
+ const base::Value* required = dict->FindKey(schema::kRequired);
+ if (required) {
+ for (const base::Value& item : required->GetList()) {
+ if (!item.is_string()) {
+ *error = "Attribute 'required' may only contain strings.";
return false;
}
- if (!IsValidSchema(dictionary_value, options, error)) {
- DCHECK(!error->empty());
+ const std::string property_name = item.GetString();
+ if (!properties || !properties->FindKey(property_name)) {
+ *error = base::StringPrintf(
+ "Attribute 'required' contains unknown property '%s'.",
+ property_name.c_str());
return false;
}
}
}
-
- if (it.key() == schema::kRef)
- has_type_or_ref = true;
- }
-
- // Check that properties in'required' are in the 'properties' object.
- if (required_properties_value) {
- for (const base::Value& value : required_properties_value->GetList()) {
- const std::string& name = value.GetString();
- if (!properties_value || !properties_value->HasKey(name)) {
- *error = "Property '" + name +
- "' was listed in 'required', but not defined in 'properties'.";
- return false;
- }
- }
- }
-
- if (!has_type_or_ref) {
- *error = "Schema must have a type or a $ref attribute";
- return false;
}
return true;
@@ -1366,8 +1425,8 @@ void Schema::MaskSensitiveValues(base::Value* value) const {
Schema Schema::Parse(const std::string& content, std::string* error) {
// Validate as a generic JSON schema, and ignore unknown attributes; they
// may become used in a future version of the schema format.
- std::unique_ptr<base::DictionaryValue> dict =
- IsValidSchema(content, Schema::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error);
+ std::unique_ptr<base::DictionaryValue> dict = Schema::ParseToDictAndValidate(
+ content, kSchemaOptionsIgnoreUnknownAttributes, error);
if (!dict)
return Schema();
@@ -1396,14 +1455,7 @@ Schema Schema::Parse(const std::string& content, std::string* error) {
}
// static
-std::unique_ptr<base::DictionaryValue> Schema::IsValidSchema(
- const std::string& schema,
- std::string* error) {
- return Schema::IsValidSchema(schema, 0, error);
-}
-
-// static
-std::unique_ptr<base::DictionaryValue> Schema::IsValidSchema(
+std::unique_ptr<base::DictionaryValue> Schema::ParseToDictAndValidate(
const std::string& schema,
int validator_options,
std::string* error) {
@@ -1418,7 +1470,7 @@ std::unique_ptr<base::DictionaryValue> Schema::IsValidSchema(
*error = "Schema must be a JSON object";
return nullptr;
}
- if (!policy::IsValidSchema(dict.get(), validator_options, error))
+ if (!IsValidSchema(dict.get(), validator_options, error))
return nullptr;
return dict;
}
diff --git a/components/policy/core/common/schema.h b/components/policy/core/common/schema.h
index 44ca58a15c..2fd16ca52c 100644
--- a/components/policy/core/common/schema.h
+++ b/components/policy/core/common/schema.h
@@ -48,6 +48,10 @@ enum SchemaOnErrorStrategy {
SCHEMA_ALLOW_INVALID,
};
+// Schema validation options for Schema::ParseToDictAndValidate().
+constexpr int kSchemaOptionsNone = 0;
+constexpr int kSchemaOptionsIgnoreUnknownAttributes = 1 << 0;
+
class Schema;
typedef std::vector<Schema> SchemaList;
@@ -57,17 +61,17 @@ typedef std::vector<Schema> SchemaList;
// Objects of this class refer to external, immutable data and are cheap to
// copy.
//
-// Schema validation is based on a subset of the JSON Schema standard.
-// TODO(crbug.com/856901): Document the supported subset of the JSON Schema
-// standard.
+// See components/policy/core/common/json_schema_constants.h for a list of
+// supported features and data types. Only these features and data-types are
+// supported and enforced. For the full schema proposal see
+// https://json-schema.org/understanding-json-schema/index.html.
+//
+// There are also these departures from the proposal:
+// - "additionalProperties": false is not supported. The value of
+// "additionalProperties" has to be a schema if present. Otherwise, the
+// behavior for unknown attributes is controlled by |SchemaOnErrorStrategy|.
class POLICY_EXPORT Schema {
public:
- enum Options {
- // Ignore unknown attributes. If this option is not set then unknown
- // attributes will make the schema validation fail.
- OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES = 1 << 0,
- };
-
// Used internally to store shared data.
class InternalStorage;
@@ -95,20 +99,18 @@ class POLICY_EXPORT Schema {
// then |schema| is valid JSON that can be parsed into a DictionaryValue,
// and that DictionaryValue can be used to build a |Schema|.
// Returns the parsed DictionaryValue when |schema| validated, otherwise
- // returns NULL. In that case, |error| contains an error description.
+ // returns nullptr. In that case, |error| contains an error description.
// For performance reasons, currently IsValidSchema() won't check the
// correctness of regular expressions used in "pattern" and
// "patternProperties" and in Validate() invalid regular expression don't
// accept any strings.
- static std::unique_ptr<base::DictionaryValue> IsValidSchema(
+ // |options| is a bitwise-OR combination of the options above (see
+ // |kSchemaOptions*| above).
+ static std::unique_ptr<base::DictionaryValue> ParseToDictAndValidate(
const std::string& schema,
+ int options,
std::string* error);
- // Same as above but with |options|, which is a bitwise-OR combination of the
- // Options above.
- static std::unique_ptr<base::DictionaryValue>
- IsValidSchema(const std::string& schema, int options, std::string* error);
-
// Returns true if this Schema is valid. Schemas returned by the methods below
// may be invalid, and in those cases the other methods must not be used.
bool valid() const { return node_ != NULL; }
diff --git a/components/policy/core/common/schema_map_unittest.cc b/components/policy/core/common/schema_map_unittest.cc
index 96916e5cc1..b4487bd550 100644
--- a/components/policy/core/common/schema_map_unittest.cc
+++ b/components/policy/core/common/schema_map_unittest.cc
@@ -20,27 +20,26 @@ namespace policy {
namespace {
const char kTestSchema[] =
- "{"
- " \"type\": \"object\","
- " \"properties\": {"
- " \"string\": { \"type\": \"string\" },"
- " \"integer\": { \"type\": \"integer\" },"
- " \"boolean\": { \"type\": \"boolean\" },"
- " \"null\": { \"type\": \"null\" },"
- " \"double\": { \"type\": \"number\" },"
- " \"list\": {"
- " \"type\": \"array\","
- " \"items\": { \"type\": \"string\" }"
- " },"
- " \"object\": {"
- " \"type\": \"object\","
- " \"properties\": {"
- " \"a\": { \"type\": \"string\" },"
- " \"b\": { \"type\": \"integer\" }"
- " }"
- " }"
- " }"
- "}";
+ R"({
+ "type": "object",
+ "properties": {
+ "string": { "type": "string" },
+ "integer": { "type": "integer" },
+ "boolean": { "type": "boolean" },
+ "double": { "type": "number" },
+ "list": {
+ "type": "array",
+ "items": { "type": "string" }
+ },
+ "object": {
+ "type": "object",
+ "properties": {
+ "a": { "type": "string" },
+ "b": { "type": "integer" }
+ }
+ }
+ }
+ })";
} // namespace
@@ -165,8 +164,6 @@ TEST_F(SchemaMapTest, FilterBundle) {
POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
map.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
- map.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
- POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(), nullptr);
map.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1.2), nullptr);
base::DictionaryValue dict;
@@ -217,12 +214,13 @@ TEST_F(SchemaMapTest, FilterBundle) {
TEST_F(SchemaMapTest, LegacyComponents) {
std::string error;
Schema schema = Schema::Parse(
- "{"
- " \"type\":\"object\","
- " \"properties\": {"
- " \"String\": { \"type\": \"string\" }"
- " }"
- "}", &error);
+ R"({
+ "type": "object",
+ "properties": {
+ "String": { "type": "string" }
+ }
+ })",
+ &error);
ASSERT_TRUE(schema.valid()) << error;
DomainMap domain_map;
diff --git a/components/policy/core/common/schema_registry_unittest.cc b/components/policy/core/common/schema_registry_unittest.cc
index a1259765ac..298b583035 100644
--- a/components/policy/core/common/schema_registry_unittest.cc
+++ b/components/policy/core/common/schema_registry_unittest.cc
@@ -21,27 +21,26 @@ namespace policy {
namespace {
const char kTestSchema[] =
- "{"
- " \"type\": \"object\","
- " \"properties\": {"
- " \"string\": { \"type\": \"string\" },"
- " \"integer\": { \"type\": \"integer\" },"
- " \"boolean\": { \"type\": \"boolean\" },"
- " \"null\": { \"type\": \"null\" },"
- " \"double\": { \"type\": \"number\" },"
- " \"list\": {"
- " \"type\": \"array\","
- " \"items\": { \"type\": \"string\" }"
- " },"
- " \"object\": {"
- " \"type\": \"object\","
- " \"properties\": {"
- " \"a\": { \"type\": \"string\" },"
- " \"b\": { \"type\": \"integer\" }"
- " }"
- " }"
- " }"
- "}";
+ R"({
+ "type": "object",
+ "properties": {
+ "string": { "type": "string" },
+ "integer": { "type": "integer" },
+ "boolean": { "type": "boolean" },
+ "double": { "type": "number" },
+ "list": {
+ "type": "array",
+ "items": { "type": "string" }
+ },
+ "object": {
+ "type": "object",
+ "properties": {
+ "a": { "type": "string" },
+ "b": { "type": "integer" }
+ }
+ }
+ }
+ })";
class MockSchemaRegistryObserver : public SchemaRegistry::Observer {
public:
diff --git a/components/policy/core/common/schema_unittest.cc b/components/policy/core/common/schema_unittest.cc
index 1fe75c7647..9c6749cc59 100644
--- a/components/policy/core/common/schema_unittest.cc
+++ b/components/policy/core/common/schema_unittest.cc
@@ -28,7 +28,6 @@ const char kTestSchema[] = R"({
"properties": {
"Boolean": { "type": "boolean" },
"Integer": { "type": "integer" },
- "Null": { "type": "null" },
"Number": { "type": "number" },
"String": { "type": "string" },
"Array": {
@@ -320,10 +319,6 @@ TEST(SchemaTest, ValidSchema) {
ASSERT_TRUE(sub.valid());
EXPECT_EQ(base::Value::Type::INTEGER, sub.type());
- sub = schema.GetProperty("Null");
- ASSERT_TRUE(sub.valid());
- EXPECT_EQ(base::Value::Type::NONE, sub.type());
-
sub = schema.GetProperty("Number");
ASSERT_TRUE(sub.valid());
EXPECT_EQ(base::Value::Type::DOUBLE, sub.type());
@@ -418,7 +413,6 @@ TEST(SchemaTest, ValidSchema) {
{ "IntegerWithEnums", base::Value::Type::INTEGER },
{ "IntegerWithEnumsGaps", base::Value::Type::INTEGER },
{ "IntegerWithRange", base::Value::Type::INTEGER },
- { "Null", base::Value::Type::NONE },
{ "Number", base::Value::Type::DOUBLE },
{ "Object", base::Value::Type::DICTIONARY },
{ "ObjectOfArray", base::Value::Type::DICTIONARY },
@@ -469,7 +463,6 @@ TEST(SchemaTest, Lookups) {
schema = Schema::Parse(R"({
"type": "object",
"properties": {
- "bb" : { "type": "null" },
"aa" : { "type": "boolean" },
"abab" : { "type": "string" },
"ab" : { "type": "number" },
@@ -491,7 +484,6 @@ TEST(SchemaTest, Lookups) {
{ "ab", base::Value::Type::DOUBLE },
{ "aba", base::Value::Type::INTEGER },
{ "abab", base::Value::Type::STRING },
- { "bb", base::Value::Type::NONE },
};
for (size_t i = 0; i < arraysize(kExpectedKeys); ++i) {
Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
@@ -702,7 +694,6 @@ TEST(SchemaTest, Validate) {
bundle.Clear();
bundle.SetBoolean("Boolean", true);
bundle.SetInteger("Integer", 123);
- bundle.Set("Null", std::make_unique<base::Value>());
bundle.SetDouble("Number", 3.14);
bundle.SetString("String", "omg");
@@ -1357,163 +1348,168 @@ TEST(SchemaTest, RangedRestriction) {
})")));
}
-TEST(SchemaTest, IsValidSchema) {
+TEST(SchemaTest, ParseToDictAndValidate) {
std::string error;
- EXPECT_FALSE(Schema::IsValidSchema("", &error));
- EXPECT_FALSE(Schema::IsValidSchema("\0", &error));
- EXPECT_FALSE(Schema::IsValidSchema("string", &error));
- EXPECT_FALSE(Schema::IsValidSchema("\"string\"", &error));
- EXPECT_FALSE(Schema::IsValidSchema("[]", &error));
- EXPECT_FALSE(Schema::IsValidSchema("{}", &error));
- EXPECT_FALSE(Schema::IsValidSchema("{ \"type\": 123 }", &error));
- EXPECT_FALSE(Schema::IsValidSchema("{ \"type\": \"invalid\" }", &error));
+ EXPECT_FALSE(Schema::ParseToDictAndValidate("", kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate("\0", kSchemaOptionsNone, &error))
+ << error;
EXPECT_FALSE(
- Schema::IsValidSchema("{"
- " \"type\": \"object\","
- " \"properties\": []" // Invalid properties type.
- "}",
- &error));
+ Schema::ParseToDictAndValidate("string", kSchemaOptionsNone, &error))
+ << error;
EXPECT_FALSE(
- Schema::IsValidSchema("{"
- " \"type\": \"string\","
- " \"maxLength\": -1" // Must be >= 0.
- "}",
- &error));
- EXPECT_FALSE(Schema::IsValidSchema(
- "{"
- " \"type\": \"string\","
- " \"enum\": [ {} ]" // "enum" dict values must contain "name".
- "}",
- &error));
- EXPECT_FALSE(Schema::IsValidSchema(
- "{"
- " \"type\": \"string\","
- " \"enum\": [ { \"name\": {} } ]" // "enum" name must be a simple value.
- "}",
- &error));
- EXPECT_FALSE(Schema::IsValidSchema(
- "{"
- " \"type\": \"array\","
- " \"items\": [ 123 ]," // "items" must contain a schema or schemas.
- "}",
- &error));
- EXPECT_TRUE(Schema::IsValidSchema("{ \"type\": \"object\" }", &error))
+ Schema::ParseToDictAndValidate(R"("string")", kSchemaOptionsNone, &error))
<< error;
- EXPECT_TRUE(
- Schema::IsValidSchema("{ \"type\": [\"object\", \"array\"] }", &error))
+ EXPECT_FALSE(Schema::ParseToDictAndValidate("[]", kSchemaOptionsNone, &error))
<< error;
- EXPECT_TRUE(Schema::IsValidSchema(
- "{"
- " \"type\": [\"object\", \"array\"],"
- " \"properties\": {"
- " \"string-property\": {"
- " \"type\": \"string\","
- " \"minLength\": 1,"
- " \"maxLength\": 100,"
- " \"title\": \"The String Policy\","
- " \"description\": \"This policy controls the String widget.\""
- " },"
- " \"integer-property\": {"
- " \"type\": \"number\","
- " \"minimum\": 1000.0,"
- " \"maximum\": 9999.0"
- " },"
- " \"enum-property\": {"
- " \"type\": \"integer\","
- " \"enum\": [0, 1, {\"name\": 10}, 100]"
- " },"
- " \"items-property\": {"
- " \"type\": \"array\","
- " \"items\": {"
- " \"type\": \"string\""
- " }"
- " },"
- " \"items-list-property\": {"
- " \"type\": \"array\","
- " \"items\": ["
- " { \"type\": \"string\" },"
- " { \"type\": \"integer\" }"
- " ]"
- " }"
- " },"
- " \"additionalProperties\": {"
- " \"type\": \"any\""
- " }"
- "}",
- &error))
+ EXPECT_FALSE(Schema::ParseToDictAndValidate("{}", kSchemaOptionsNone, &error))
<< error;
- EXPECT_TRUE(
- Schema::IsValidSchema("{"
- " \"type\": \"object\","
- " \"patternProperties\": {"
- " \".\": { \"type\": \"any\" },"
- " \"foo\": { \"type\": \"any\" },"
- " \"^foo$\": { \"type\": \"any\" },"
- " \"foo+\": { \"type\": \"any\" },"
- " \"foo?\": { \"type\": \"any\" },"
- " \"fo{2,4}\": { \"type\": \"any\" },"
- " \"(left)|(right)\": { \"type\": \"any\" }"
- " }"
- "}",
- &error))
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(R"({ "type": 123 })",
+ kSchemaOptionsNone, &error))
<< error;
- EXPECT_TRUE(Schema::IsValidSchema(
- "{"
- " \"type\": \"object\","
- " \"unknown attribute\": \"that should just be ignored\""
- "}",
- Schema::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, &error))
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(R"({ "type": "invalid" })",
+ kSchemaOptionsNone, &error))
<< error;
- EXPECT_FALSE(Schema::IsValidSchema(
- "{"
- " \"type\": \"object\","
- " \"unknown attribute\": \"that will cause a failure\""
- "}",
- 0, &error))
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "object",
+ "properties": []
+ })", // Invalid properties type.
+ kSchemaOptionsNone, &error))
<< error;
-
- EXPECT_FALSE(Schema::IsValidSchema(R"(
- {
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "string",
+ "enum": [ {} ]
+ })", // "enum" dict values must contain "name".
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "string",
+ "enum": [ { "name": {} } ]
+ })", // "enum" name must be a simple value.
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "array",
+ "items": [ 123 ],
+ })", // "items" must contain a schema or schemas.
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_TRUE(Schema::ParseToDictAndValidate(
+ R"({ "type": "object" })", kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({ "type": ["object", "array"] })", kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "array",
+ "items": [
+ { "type": "string" },
+ { "type": "integer" }
+ ]
+ })",
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_TRUE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "object",
+ "properties": {
+ "string-property": {
+ "type": "string",
+ "title": "The String Policy",
+ "description": "This policy controls the String widget."
+ },
+ "integer-property": {
+ "type": "number"
+ },
+ "enum-property": {
+ "type": "integer",
+ "enum": [0, 1, 10, 100]
+ },
+ "items-property": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ })",
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_TRUE(Schema::ParseToDictAndValidate(
+ R"#({
+ "type": "object",
+ "patternProperties": {
+ ".": { "type": "boolean" },
+ "foo": { "type": "boolean" },
+ "^foo$": { "type": "boolean" },
+ "foo+": { "type": "boolean" },
+ "foo?": { "type": "boolean" },
+ "fo{2,4}": { "type": "boolean" },
+ "(left)|(right)": { "type": "boolean" }
+ }
+ })#",
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_TRUE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "object",
+ "unknown attribute": "that should just be ignored"
+ })",
+ kSchemaOptionsIgnoreUnknownAttributes, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
+ "type": "object",
+ "unknown attribute": "that will cause a failure"
+ })",
+ kSchemaOptionsNone, &error))
+ << error;
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
"type": "object",
"properties": {"foo": {"type": "number"}},
"required": 123
})",
- 0, &error))
+ kSchemaOptionsNone, &error))
<< error;
-
- EXPECT_FALSE(Schema::IsValidSchema(R"(
- {
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
"type": "object",
"properties": {"foo": {"type": "number"}},
"required": [ 123 ]
})",
- 0, &error))
+ kSchemaOptionsNone, &error))
<< error;
-
- EXPECT_FALSE(Schema::IsValidSchema(R"(
- {
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
"type": "object",
"properties": {"foo": {"type": "number"}},
"required": ["bar"]
})",
- 0, &error))
+ kSchemaOptionsNone, &error))
<< error;
-
- EXPECT_FALSE(Schema::IsValidSchema(R"(
- {
+ EXPECT_FALSE(Schema::ParseToDictAndValidate(
+ R"({
"type": "object",
"required": ["bar"]
})",
- 0, &error))
+ kSchemaOptionsNone, &error))
<< error;
-
- EXPECT_TRUE(Schema::IsValidSchema(R"(
- {
+ EXPECT_TRUE(Schema::ParseToDictAndValidate(
+ R"({
"type": "object",
"properties": {"foo": {"type": "number"}},
"required": ["foo"]
})",
- 0, &error))
+ kSchemaOptionsNone, &error))
<< error;
}