1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 6 #define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 7 8 #include <map> 9 #include <memory> 10 #include <string> 11 #include <vector> 12 13 #include "base/macros.h" 14 15 namespace base { 16 class DictionaryValue; 17 class ListValue; 18 class Value; 19 } 20 21 //============================================================================== 22 // This class implements a subset of JSON Schema. 23 // See: http://www.json.com/json-schema-proposal/ for more details. 24 // 25 // There is also an older JavaScript implementation of the same functionality in 26 // chrome/renderer/resources/json_schema.js. 27 // 28 // The following features of JSON Schema are not implemented: 29 // - requires 30 // - unique 31 // - disallow 32 // - union types (but replaced with 'choices') 33 // - number.maxDecimal 34 // 35 // The following properties are not applicable to the interface exposed by 36 // this class: 37 // - options 38 // - readonly 39 // - title 40 // - description 41 // - format 42 // - default 43 // - transient 44 // - hidden 45 // 46 // There are also these departures from the JSON Schema proposal: 47 // - null counts as 'unspecified' for optional values 48 // - added the 'choices' property, to allow specifying a list of possible types 49 // for a value 50 // - by default an "object" typed schema does not allow additional properties. 51 // if present, "additionalProperties" is to be a schema against which all 52 // additional properties will be validated. 53 // - regular expression supports all syntaxes that re2 accepts. 54 // See https://github.com/google/re2/blob/master/doc/syntax.txt for details. 55 //============================================================================== 56 class JSONSchemaValidator { 57 public: 58 // Details about a validation error. 59 struct Error { 60 Error(); 61 62 explicit Error(const std::string& message); 63 64 Error(const std::string& path, const std::string& message); 65 66 // The path to the location of the error in the JSON structure. 67 std::string path; 68 69 // An english message describing the error. 70 std::string message; 71 }; 72 73 enum Options { 74 // Ignore unknown attributes. If this option is not set then unknown 75 // attributes will make the schema validation fail. 76 OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES = 1 << 0, 77 }; 78 79 // Error messages. 80 static const char kUnknownTypeReference[]; 81 static const char kInvalidChoice[]; 82 static const char kInvalidEnum[]; 83 static const char kObjectPropertyIsRequired[]; 84 static const char kUnexpectedProperty[]; 85 static const char kArrayMinItems[]; 86 static const char kArrayMaxItems[]; 87 static const char kArrayItemRequired[]; 88 static const char kStringMinLength[]; 89 static const char kStringMaxLength[]; 90 static const char kStringPattern[]; 91 static const char kNumberMinimum[]; 92 static const char kNumberMaximum[]; 93 static const char kInvalidType[]; 94 static const char kInvalidTypeIntegerNumber[]; 95 static const char kInvalidRegex[]; 96 97 // Classifies a Value as one of the JSON schema primitive types. 98 static std::string GetJSONSchemaType(const base::Value* value); 99 100 // Utility methods to format error messages. The first method can have one 101 // wildcard represented by '*', which is replaced with s1. The second method 102 // can have two, which are replaced by s1 and s2. 103 static std::string FormatErrorMessage(const std::string& format, 104 const std::string& s1); 105 static std::string FormatErrorMessage(const std::string& format, 106 const std::string& s1, 107 const std::string& s2); 108 109 // Verifies if |schema| is a valid JSON v3 schema. When this validation passes 110 // then |schema| is valid JSON that can be parsed into a DictionaryValue, 111 // and that DictionaryValue can be used to build a JSONSchemaValidator. 112 // Returns the parsed DictionaryValue when |schema| validated, otherwise 113 // returns NULL. In that case, |error| contains an error description. 114 // For performance reasons, currently IsValidSchema() won't check the 115 // correctness of regular expressions used in "pattern" and 116 // "patternProperties" and in Validate() invalid regular expression don't 117 // accept any strings. 118 static std::unique_ptr<base::DictionaryValue> IsValidSchema( 119 const std::string& schema, 120 std::string* error); 121 122 // Same as above but with |options|, which is a bitwise-OR combination of the 123 // Options above. 124 static std::unique_ptr<base::DictionaryValue> 125 IsValidSchema(const std::string& schema, int options, std::string* error); 126 127 // Creates a validator for the specified schema. 128 // 129 // NOTE: This constructor assumes that |schema| is well formed and valid. 130 // Errors will result in CHECK at runtime; this constructor should not be used 131 // with untrusted schemas. 132 explicit JSONSchemaValidator(base::DictionaryValue* schema); 133 134 // Creates a validator for the specified schema and user-defined types. Each 135 // type must be a valid JSONSchema type description with an additional "id" 136 // field. Schema objects in |schema| can refer to these types with the "$ref" 137 // property. 138 // 139 // NOTE: This constructor assumes that |schema| and |types| are well-formed 140 // and valid. Errors will result in CHECK at runtime; this constructor should 141 // not be used with untrusted schemas. 142 JSONSchemaValidator(base::DictionaryValue* schema, base::ListValue* types); 143 144 ~JSONSchemaValidator(); 145 146 // Whether the validator allows additional items for objects and lists, beyond 147 // those defined by their schema, by default. 148 // 149 // This setting defaults to false: all items in an instance list or object 150 // must be defined by the corresponding schema. 151 // 152 // This setting can be overridden on individual object and list schemas by 153 // setting the "additionalProperties" field. default_allow_additional_properties()154 bool default_allow_additional_properties() const { 155 return default_allow_additional_properties_; 156 } 157 set_default_allow_additional_properties(bool val)158 void set_default_allow_additional_properties(bool val) { 159 default_allow_additional_properties_ = val; 160 } 161 162 // Returns any errors from the last call to to Validate(). errors()163 const std::vector<Error>& errors() const { 164 return errors_; 165 } 166 167 // Validates a JSON value. Returns true if the instance is valid, false 168 // otherwise. If false is returned any errors are available from the errors() 169 // getter. 170 bool Validate(const base::Value* instance); 171 172 private: 173 typedef std::map<std::string, const base::DictionaryValue*> TypeMap; 174 175 // Each of the below methods handle a subset of the validation process. The 176 // path paramater is the path to |instance| from the root of the instance tree 177 // and is used in error messages. 178 179 // Validates any instance node against any schema node. This is called for 180 // every node in the instance tree, and it just decides which of the more 181 // detailed methods to call. 182 void Validate(const base::Value* instance, 183 const base::DictionaryValue* schema, 184 const std::string& path); 185 186 // Validates a node against a list of possible schemas. If any one of the 187 // schemas match, the node is valid. 188 void ValidateChoices(const base::Value* instance, 189 const base::ListValue* choices, 190 const std::string& path); 191 192 // Validates a node against a list of exact primitive values, eg 42, "foobar". 193 void ValidateEnum(const base::Value* instance, 194 const base::ListValue* choices, 195 const std::string& path); 196 197 // Validates a JSON object against an object schema node. 198 void ValidateObject(const base::DictionaryValue* instance, 199 const base::DictionaryValue* schema, 200 const std::string& path); 201 202 // Validates a JSON array against an array schema node. 203 void ValidateArray(const base::ListValue* instance, 204 const base::DictionaryValue* schema, 205 const std::string& path); 206 207 // Validates a JSON array against an array schema node configured to be a 208 // tuple. In a tuple, there is one schema node for each item expected in the 209 // array. 210 void ValidateTuple(const base::ListValue* instance, 211 const base::DictionaryValue* schema, 212 const std::string& path); 213 214 // Validate a JSON string against a string schema node. 215 void ValidateString(const base::Value* instance, 216 const base::DictionaryValue* schema, 217 const std::string& path); 218 219 // Validate a JSON number against a number schema node. 220 void ValidateNumber(const base::Value* instance, 221 const base::DictionaryValue* schema, 222 const std::string& path); 223 224 // Validates that the JSON node |instance| has |expected_type|. 225 bool ValidateType(const base::Value* instance, 226 const std::string& expected_type, 227 const std::string& path); 228 229 // Returns true if |schema| will allow additional items of any type. 230 bool SchemaAllowsAnyAdditionalItems( 231 const base::DictionaryValue* schema, 232 const base::DictionaryValue** addition_items_schema); 233 234 // The root schema node. 235 base::DictionaryValue* schema_root_; 236 237 // Map of user-defined name to type. 238 TypeMap types_; 239 240 // Whether we allow additional properties on objects by default. This can be 241 // overridden by the allow_additional_properties flag on an Object schema. 242 bool default_allow_additional_properties_; 243 244 // Errors accumulated since the last call to Validate(). 245 std::vector<Error> errors_; 246 247 248 DISALLOW_COPY_AND_ASSIGN(JSONSchemaValidator); 249 }; 250 251 #endif // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 252