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#include "components/json_schema/json_schema_validator.h" 6 7#include <algorithm> 8#include <cfloat> 9#include <cmath> 10 11#include "base/json/json_reader.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/string_util.h" 14#include "base/strings/stringprintf.h" 15#include "base/values.h" 16#include "components/json_schema/json_schema_constants.h" 17 18namespace schema = json_schema_constants; 19 20namespace { 21 22double GetNumberValue(const base::Value* value) { 23 double result = 0; 24 CHECK(value->GetAsDouble(&result)) 25 << "Unexpected value type: " << value->GetType(); 26 return result; 27} 28 29bool IsValidType(const std::string& type) { 30 static const char* kValidTypes[] = { 31 schema::kAny, 32 schema::kArray, 33 schema::kBoolean, 34 schema::kInteger, 35 schema::kNull, 36 schema::kNumber, 37 schema::kObject, 38 schema::kString, 39 }; 40 const char** end = kValidTypes + arraysize(kValidTypes); 41 return std::find(kValidTypes, end, type) != end; 42} 43 44// Maps a schema attribute name to its expected type. 45struct ExpectedType { 46 const char* key; 47 base::Value::Type type; 48}; 49 50// Helper for std::lower_bound. 51bool CompareToString(const ExpectedType& entry, const std::string& key) { 52 return entry.key < key; 53} 54 55// If |value| is a dictionary, returns the "name" attribute of |value| or NULL 56// if |value| does not contain a "name" attribute. Otherwise, returns |value|. 57const base::Value* ExtractNameFromDictionary(const base::Value* value) { 58 const base::DictionaryValue* value_dict = NULL; 59 const base::Value* name_value = NULL; 60 if (value->GetAsDictionary(&value_dict)) { 61 value_dict->Get("name", &name_value); 62 return name_value; 63 } 64 return value; 65} 66 67bool IsValidSchema(const base::DictionaryValue* dict, 68 int options, 69 std::string* error) { 70 // This array must be sorted, so that std::lower_bound can perform a 71 // binary search. 72 static const ExpectedType kExpectedTypes[] = { 73 // Note: kRef == "$ref", kSchema == "$schema" 74 { schema::kRef, base::Value::TYPE_STRING }, 75 { schema::kSchema, base::Value::TYPE_STRING }, 76 77 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY }, 78 { schema::kChoices, base::Value::TYPE_LIST }, 79 { schema::kDescription, base::Value::TYPE_STRING }, 80 { schema::kEnum, base::Value::TYPE_LIST }, 81 { schema::kId, base::Value::TYPE_STRING }, 82 { schema::kMaxItems, base::Value::TYPE_INTEGER }, 83 { schema::kMaxLength, base::Value::TYPE_INTEGER }, 84 { schema::kMaximum, base::Value::TYPE_DOUBLE }, 85 { schema::kMinItems, base::Value::TYPE_INTEGER }, 86 { schema::kMinLength, base::Value::TYPE_INTEGER }, 87 { schema::kMinimum, base::Value::TYPE_DOUBLE }, 88 { schema::kOptional, base::Value::TYPE_BOOLEAN }, 89 { schema::kProperties, base::Value::TYPE_DICTIONARY }, 90 { schema::kTitle, base::Value::TYPE_STRING }, 91 }; 92 93 bool has_type_or_ref = false; 94 const base::ListValue* list_value = NULL; 95 const base::DictionaryValue* dictionary_value = NULL; 96 std::string string_value; 97 98 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { 99 // Validate the "type" attribute, which may be a string or a list. 100 if (it.key() == schema::kType) { 101 switch (it.value().GetType()) { 102 case base::Value::TYPE_STRING: 103 it.value().GetAsString(&string_value); 104 if (!IsValidType(string_value)) { 105 *error = "Invalid value for type attribute"; 106 return false; 107 } 108 break; 109 case base::Value::TYPE_LIST: 110 it.value().GetAsList(&list_value); 111 for (size_t i = 0; i < list_value->GetSize(); ++i) { 112 if (!list_value->GetString(i, &string_value) || 113 !IsValidType(string_value)) { 114 *error = "Invalid value for type attribute"; 115 return false; 116 } 117 } 118 break; 119 default: 120 *error = "Invalid value for type attribute"; 121 return false; 122 } 123 has_type_or_ref = true; 124 continue; 125 } 126 127 // Validate the "items" attribute, which is a schema or a list of schemas. 128 if (it.key() == schema::kItems) { 129 if (it.value().GetAsDictionary(&dictionary_value)) { 130 if (!IsValidSchema(dictionary_value, options, error)) { 131 DCHECK(!error->empty()); 132 return false; 133 } 134 } else if (it.value().GetAsList(&list_value)) { 135 for (size_t i = 0; i < list_value->GetSize(); ++i) { 136 if (!list_value->GetDictionary(i, &dictionary_value)) { 137 *error = base::StringPrintf( 138 "Invalid entry in items attribute at index %d", 139 static_cast<int>(i)); 140 return false; 141 } 142 if (!IsValidSchema(dictionary_value, options, error)) { 143 DCHECK(!error->empty()); 144 return false; 145 } 146 } 147 } else { 148 *error = "Invalid value for items attribute"; 149 return false; 150 } 151 continue; 152 } 153 154 // All the other attributes have a single valid type. 155 const ExpectedType* end = kExpectedTypes + arraysize(kExpectedTypes); 156 const ExpectedType* entry = std::lower_bound( 157 kExpectedTypes, end, it.key(), CompareToString); 158 if (entry == end || entry->key != it.key()) { 159 if (options & JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES) 160 continue; 161 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); 162 return false; 163 } 164 165 if (!it.value().IsType(entry->type)) { 166 *error = base::StringPrintf("Invalid value for %s attribute", 167 it.key().c_str()); 168 return false; 169 } 170 171 // base::Value::TYPE_INTEGER attributes must be >= 0. 172 // This applies to "minItems", "maxItems", "minLength" and "maxLength". 173 if (it.value().IsType(base::Value::TYPE_INTEGER)) { 174 int integer_value; 175 it.value().GetAsInteger(&integer_value); 176 if (integer_value < 0) { 177 *error = base::StringPrintf("Value of %s must be >= 0, got %d", 178 it.key().c_str(), integer_value); 179 return false; 180 } 181 } 182 183 // Validate the "properties" attribute. Each entry maps a key to a schema. 184 if (it.key() == schema::kProperties) { 185 it.value().GetAsDictionary(&dictionary_value); 186 for (base::DictionaryValue::Iterator it(*dictionary_value); 187 !it.IsAtEnd(); it.Advance()) { 188 if (!it.value().GetAsDictionary(&dictionary_value)) { 189 *error = "Invalid value for properties attribute"; 190 return false; 191 } 192 if (!IsValidSchema(dictionary_value, options, error)) { 193 DCHECK(!error->empty()); 194 return false; 195 } 196 } 197 } 198 199 // Validate "additionalProperties" attribute, which is a schema. 200 if (it.key() == schema::kAdditionalProperties) { 201 it.value().GetAsDictionary(&dictionary_value); 202 if (!IsValidSchema(dictionary_value, options, error)) { 203 DCHECK(!error->empty()); 204 return false; 205 } 206 } 207 208 // Validate the values contained in an "enum" attribute. 209 if (it.key() == schema::kEnum) { 210 it.value().GetAsList(&list_value); 211 for (size_t i = 0; i < list_value->GetSize(); ++i) { 212 const base::Value* value = NULL; 213 list_value->Get(i, &value); 214 // Sometimes the enum declaration is a dictionary with the enum value 215 // under "name". 216 value = ExtractNameFromDictionary(value); 217 if (!value) { 218 *error = "Invalid value in enum attribute"; 219 return false; 220 } 221 switch (value->GetType()) { 222 case base::Value::TYPE_NULL: 223 case base::Value::TYPE_BOOLEAN: 224 case base::Value::TYPE_INTEGER: 225 case base::Value::TYPE_DOUBLE: 226 case base::Value::TYPE_STRING: 227 break; 228 default: 229 *error = "Invalid value in enum attribute"; 230 return false; 231 } 232 } 233 } 234 235 // Validate the schemas contained in a "choices" attribute. 236 if (it.key() == schema::kChoices) { 237 it.value().GetAsList(&list_value); 238 for (size_t i = 0; i < list_value->GetSize(); ++i) { 239 if (!list_value->GetDictionary(i, &dictionary_value)) { 240 *error = "Invalid choices attribute"; 241 return false; 242 } 243 if (!IsValidSchema(dictionary_value, options, error)) { 244 DCHECK(!error->empty()); 245 return false; 246 } 247 } 248 } 249 250 if (it.key() == schema::kRef) 251 has_type_or_ref = true; 252 } 253 254 if (!has_type_or_ref) { 255 *error = "Schema must have a type or a $ref attribute"; 256 return false; 257 } 258 259 return true; 260} 261 262} // namespace 263 264 265JSONSchemaValidator::Error::Error() { 266} 267 268JSONSchemaValidator::Error::Error(const std::string& message) 269 : path(message) { 270} 271 272JSONSchemaValidator::Error::Error(const std::string& path, 273 const std::string& message) 274 : path(path), message(message) { 275} 276 277 278const char JSONSchemaValidator::kUnknownTypeReference[] = 279 "Unknown schema reference: *."; 280const char JSONSchemaValidator::kInvalidChoice[] = 281 "Value does not match any valid type choices."; 282const char JSONSchemaValidator::kInvalidEnum[] = 283 "Value does not match any valid enum choices."; 284const char JSONSchemaValidator::kObjectPropertyIsRequired[] = 285 "Property is required."; 286const char JSONSchemaValidator::kUnexpectedProperty[] = 287 "Unexpected property."; 288const char JSONSchemaValidator::kArrayMinItems[] = 289 "Array must have at least * items."; 290const char JSONSchemaValidator::kArrayMaxItems[] = 291 "Array must not have more than * items."; 292const char JSONSchemaValidator::kArrayItemRequired[] = 293 "Item is required."; 294const char JSONSchemaValidator::kStringMinLength[] = 295 "String must be at least * characters long."; 296const char JSONSchemaValidator::kStringMaxLength[] = 297 "String must not be more than * characters long."; 298const char JSONSchemaValidator::kStringPattern[] = 299 "String must match the pattern: *."; 300const char JSONSchemaValidator::kNumberMinimum[] = 301 "Value must not be less than *."; 302const char JSONSchemaValidator::kNumberMaximum[] = 303 "Value must not be greater than *."; 304const char JSONSchemaValidator::kInvalidType[] = 305 "Expected '*' but got '*'."; 306const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = 307 "Expected 'integer' but got 'number', consider using Math.round()."; 308 309 310// static 311std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { 312 switch (value->GetType()) { 313 case base::Value::TYPE_NULL: 314 return schema::kNull; 315 case base::Value::TYPE_BOOLEAN: 316 return schema::kBoolean; 317 case base::Value::TYPE_INTEGER: 318 return schema::kInteger; 319 case base::Value::TYPE_DOUBLE: { 320 double double_value = 0; 321 value->GetAsDouble(&double_value); 322 if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) && 323 double_value == floor(double_value)) { 324 return schema::kInteger; 325 } else { 326 return schema::kNumber; 327 } 328 } 329 case base::Value::TYPE_STRING: 330 return schema::kString; 331 case base::Value::TYPE_DICTIONARY: 332 return schema::kObject; 333 case base::Value::TYPE_LIST: 334 return schema::kArray; 335 default: 336 NOTREACHED() << "Unexpected value type: " << value->GetType(); 337 return std::string(); 338 } 339} 340 341// static 342std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, 343 const std::string& s1) { 344 std::string ret_val = format; 345 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); 346 return ret_val; 347} 348 349// static 350std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, 351 const std::string& s1, 352 const std::string& s2) { 353 std::string ret_val = format; 354 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); 355 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); 356 return ret_val; 357} 358 359// static 360scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema( 361 const std::string& schema, 362 std::string* error) { 363 return JSONSchemaValidator::IsValidSchema(schema, 0, error); 364} 365 366// static 367scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema( 368 const std::string& schema, 369 int validator_options, 370 std::string* error) { 371 base::JSONParserOptions json_options = base::JSON_PARSE_RFC; 372 scoped_ptr<base::Value> json( 373 base::JSONReader::ReadAndReturnError(schema, json_options, NULL, error)); 374 if (!json) 375 return scoped_ptr<base::DictionaryValue>(); 376 base::DictionaryValue* dict = NULL; 377 if (!json->GetAsDictionary(&dict)) { 378 *error = "Schema must be a JSON object"; 379 return scoped_ptr<base::DictionaryValue>(); 380 } 381 if (!::IsValidSchema(dict, validator_options, error)) 382 return scoped_ptr<base::DictionaryValue>(); 383 ignore_result(json.release()); 384 return make_scoped_ptr(dict); 385} 386 387JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema) 388 : schema_root_(schema), default_allow_additional_properties_(false) { 389} 390 391JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema, 392 base::ListValue* types) 393 : schema_root_(schema), default_allow_additional_properties_(false) { 394 if (!types) 395 return; 396 397 for (size_t i = 0; i < types->GetSize(); ++i) { 398 base::DictionaryValue* type = NULL; 399 CHECK(types->GetDictionary(i, &type)); 400 401 std::string id; 402 CHECK(type->GetString(schema::kId, &id)); 403 404 CHECK(types_.find(id) == types_.end()); 405 types_[id] = type; 406 } 407} 408 409JSONSchemaValidator::~JSONSchemaValidator() {} 410 411bool JSONSchemaValidator::Validate(const base::Value* instance) { 412 errors_.clear(); 413 Validate(instance, schema_root_, std::string()); 414 return errors_.empty(); 415} 416 417void JSONSchemaValidator::Validate(const base::Value* instance, 418 const base::DictionaryValue* schema, 419 const std::string& path) { 420 // If this schema defines itself as reference type, save it in this.types. 421 std::string id; 422 if (schema->GetString(schema::kId, &id)) { 423 TypeMap::iterator iter = types_.find(id); 424 if (iter == types_.end()) 425 types_[id] = schema; 426 else 427 DCHECK(iter->second == schema); 428 } 429 430 // If the schema has a $ref property, the instance must validate against 431 // that schema. It must be present in types_ to be referenced. 432 std::string ref; 433 if (schema->GetString(schema::kRef, &ref)) { 434 TypeMap::iterator type = types_.find(ref); 435 if (type == types_.end()) { 436 errors_.push_back( 437 Error(path, FormatErrorMessage(kUnknownTypeReference, ref))); 438 } else { 439 Validate(instance, type->second, path); 440 } 441 return; 442 } 443 444 // If the schema has a choices property, the instance must validate against at 445 // least one of the items in that array. 446 const base::ListValue* choices = NULL; 447 if (schema->GetList(schema::kChoices, &choices)) { 448 ValidateChoices(instance, choices, path); 449 return; 450 } 451 452 // If the schema has an enum property, the instance must be one of those 453 // values. 454 const base::ListValue* enumeration = NULL; 455 if (schema->GetList(schema::kEnum, &enumeration)) { 456 ValidateEnum(instance, enumeration, path); 457 return; 458 } 459 460 std::string type; 461 schema->GetString(schema::kType, &type); 462 CHECK(!type.empty()); 463 if (type != schema::kAny) { 464 if (!ValidateType(instance, type, path)) 465 return; 466 467 // These casts are safe because of checks in ValidateType(). 468 if (type == schema::kObject) { 469 ValidateObject(static_cast<const base::DictionaryValue*>(instance), 470 schema, 471 path); 472 } else if (type == schema::kArray) { 473 ValidateArray(static_cast<const base::ListValue*>(instance), 474 schema, path); 475 } else if (type == schema::kString) { 476 // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies 477 // GetAsString() can safely be carried out, not that it's a StringValue. 478 ValidateString(instance, schema, path); 479 } else if (type == schema::kNumber || type == schema::kInteger) { 480 ValidateNumber(instance, schema, path); 481 } else if (type != schema::kBoolean && type != schema::kNull) { 482 NOTREACHED() << "Unexpected type: " << type; 483 } 484 } 485} 486 487void JSONSchemaValidator::ValidateChoices(const base::Value* instance, 488 const base::ListValue* choices, 489 const std::string& path) { 490 size_t original_num_errors = errors_.size(); 491 492 for (size_t i = 0; i < choices->GetSize(); ++i) { 493 const base::DictionaryValue* choice = NULL; 494 CHECK(choices->GetDictionary(i, &choice)); 495 496 Validate(instance, choice, path); 497 if (errors_.size() == original_num_errors) 498 return; 499 500 // We discard the error from each choice. We only want to know if any of the 501 // validations succeeded. 502 errors_.resize(original_num_errors); 503 } 504 505 // Now add a generic error that no choices matched. 506 errors_.push_back(Error(path, kInvalidChoice)); 507 return; 508} 509 510void JSONSchemaValidator::ValidateEnum(const base::Value* instance, 511 const base::ListValue* choices, 512 const std::string& path) { 513 for (size_t i = 0; i < choices->GetSize(); ++i) { 514 const base::Value* choice = NULL; 515 CHECK(choices->Get(i, &choice)); 516 // Sometimes the enum declaration is a dictionary with the enum value under 517 // "name". 518 choice = ExtractNameFromDictionary(choice); 519 if (!choice) { 520 NOTREACHED(); 521 } 522 switch (choice->GetType()) { 523 case base::Value::TYPE_NULL: 524 case base::Value::TYPE_BOOLEAN: 525 case base::Value::TYPE_STRING: 526 if (instance->Equals(choice)) 527 return; 528 break; 529 530 case base::Value::TYPE_INTEGER: 531 case base::Value::TYPE_DOUBLE: 532 if (instance->IsType(base::Value::TYPE_INTEGER) || 533 instance->IsType(base::Value::TYPE_DOUBLE)) { 534 if (GetNumberValue(choice) == GetNumberValue(instance)) 535 return; 536 } 537 break; 538 539 default: 540 NOTREACHED() << "Unexpected type in enum: " << choice->GetType(); 541 } 542 } 543 544 errors_.push_back(Error(path, kInvalidEnum)); 545} 546 547void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, 548 const base::DictionaryValue* schema, 549 const std::string& path) { 550 const base::DictionaryValue* properties = NULL; 551 schema->GetDictionary(schema::kProperties, &properties); 552 if (properties) { 553 for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); 554 it.Advance()) { 555 std::string prop_path = path.empty() ? it.key() : (path + "." + it.key()); 556 const base::DictionaryValue* prop_schema = NULL; 557 CHECK(it.value().GetAsDictionary(&prop_schema)); 558 559 const base::Value* prop_value = NULL; 560 if (instance->Get(it.key(), &prop_value)) { 561 Validate(prop_value, prop_schema, prop_path); 562 } else { 563 // Properties are required unless there is an optional field set to 564 // 'true'. 565 bool is_optional = false; 566 prop_schema->GetBoolean(schema::kOptional, &is_optional); 567 if (!is_optional) { 568 errors_.push_back(Error(prop_path, kObjectPropertyIsRequired)); 569 } 570 } 571 } 572 } 573 574 const base::DictionaryValue* additional_properties_schema = NULL; 575 if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) 576 return; 577 578 // Validate additional properties. 579 for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd(); 580 it.Advance()) { 581 if (properties && properties->HasKey(it.key())) 582 continue; 583 584 std::string prop_path = path.empty() ? it.key() : path + "." + it.key(); 585 if (!additional_properties_schema) { 586 errors_.push_back(Error(prop_path, kUnexpectedProperty)); 587 } else { 588 Validate(&it.value(), additional_properties_schema, prop_path); 589 } 590 } 591} 592 593void JSONSchemaValidator::ValidateArray(const base::ListValue* instance, 594 const base::DictionaryValue* schema, 595 const std::string& path) { 596 const base::DictionaryValue* single_type = NULL; 597 size_t instance_size = instance->GetSize(); 598 if (schema->GetDictionary(schema::kItems, &single_type)) { 599 int min_items = 0; 600 if (schema->GetInteger(schema::kMinItems, &min_items)) { 601 CHECK(min_items >= 0); 602 if (instance_size < static_cast<size_t>(min_items)) { 603 errors_.push_back(Error(path, FormatErrorMessage( 604 kArrayMinItems, base::IntToString(min_items)))); 605 } 606 } 607 608 int max_items = 0; 609 if (schema->GetInteger(schema::kMaxItems, &max_items)) { 610 CHECK(max_items >= 0); 611 if (instance_size > static_cast<size_t>(max_items)) { 612 errors_.push_back(Error(path, FormatErrorMessage( 613 kArrayMaxItems, base::IntToString(max_items)))); 614 } 615 } 616 617 // If the items property is a single schema, each item in the array must 618 // validate against that schema. 619 for (size_t i = 0; i < instance_size; ++i) { 620 const base::Value* item = NULL; 621 CHECK(instance->Get(i, &item)); 622 std::string i_str = base::Uint64ToString(i); 623 std::string item_path = path.empty() ? i_str : (path + "." + i_str); 624 Validate(item, single_type, item_path); 625 } 626 627 return; 628 } 629 630 // Otherwise, the list must be a tuple type, where each item in the list has a 631 // particular schema. 632 ValidateTuple(instance, schema, path); 633} 634 635void JSONSchemaValidator::ValidateTuple(const base::ListValue* instance, 636 const base::DictionaryValue* schema, 637 const std::string& path) { 638 const base::ListValue* tuple_type = NULL; 639 schema->GetList(schema::kItems, &tuple_type); 640 size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0; 641 if (tuple_type) { 642 for (size_t i = 0; i < tuple_size; ++i) { 643 std::string i_str = base::Uint64ToString(i); 644 std::string item_path = path.empty() ? i_str : (path + "." + i_str); 645 const base::DictionaryValue* item_schema = NULL; 646 CHECK(tuple_type->GetDictionary(i, &item_schema)); 647 const base::Value* item_value = NULL; 648 instance->Get(i, &item_value); 649 if (item_value && item_value->GetType() != base::Value::TYPE_NULL) { 650 Validate(item_value, item_schema, item_path); 651 } else { 652 bool is_optional = false; 653 item_schema->GetBoolean(schema::kOptional, &is_optional); 654 if (!is_optional) { 655 errors_.push_back(Error(item_path, kArrayItemRequired)); 656 return; 657 } 658 } 659 } 660 } 661 662 const base::DictionaryValue* additional_properties_schema = NULL; 663 if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) 664 return; 665 666 size_t instance_size = instance->GetSize(); 667 if (additional_properties_schema) { 668 // Any additional properties must validate against the additionalProperties 669 // schema. 670 for (size_t i = tuple_size; i < instance_size; ++i) { 671 std::string i_str = base::Uint64ToString(i); 672 std::string item_path = path.empty() ? i_str : (path + "." + i_str); 673 const base::Value* item_value = NULL; 674 CHECK(instance->Get(i, &item_value)); 675 Validate(item_value, additional_properties_schema, item_path); 676 } 677 } else if (instance_size > tuple_size) { 678 errors_.push_back(Error(path, FormatErrorMessage( 679 kArrayMaxItems, base::Uint64ToString(tuple_size)))); 680 } 681} 682 683void JSONSchemaValidator::ValidateString(const base::Value* instance, 684 const base::DictionaryValue* schema, 685 const std::string& path) { 686 std::string value; 687 CHECK(instance->GetAsString(&value)); 688 689 int min_length = 0; 690 if (schema->GetInteger(schema::kMinLength, &min_length)) { 691 CHECK(min_length >= 0); 692 if (value.size() < static_cast<size_t>(min_length)) { 693 errors_.push_back(Error(path, FormatErrorMessage( 694 kStringMinLength, base::IntToString(min_length)))); 695 } 696 } 697 698 int max_length = 0; 699 if (schema->GetInteger(schema::kMaxLength, &max_length)) { 700 CHECK(max_length >= 0); 701 if (value.size() > static_cast<size_t>(max_length)) { 702 errors_.push_back(Error(path, FormatErrorMessage( 703 kStringMaxLength, base::IntToString(max_length)))); 704 } 705 } 706 707 CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported."; 708} 709 710void JSONSchemaValidator::ValidateNumber(const base::Value* instance, 711 const base::DictionaryValue* schema, 712 const std::string& path) { 713 double value = GetNumberValue(instance); 714 715 // TODO(aa): It would be good to test that the double is not infinity or nan, 716 // but isnan and isinf aren't defined on Windows. 717 718 double minimum = 0; 719 if (schema->GetDouble(schema::kMinimum, &minimum)) { 720 if (value < minimum) 721 errors_.push_back(Error(path, FormatErrorMessage( 722 kNumberMinimum, base::DoubleToString(minimum)))); 723 } 724 725 double maximum = 0; 726 if (schema->GetDouble(schema::kMaximum, &maximum)) { 727 if (value > maximum) 728 errors_.push_back(Error(path, FormatErrorMessage( 729 kNumberMaximum, base::DoubleToString(maximum)))); 730 } 731} 732 733bool JSONSchemaValidator::ValidateType(const base::Value* instance, 734 const std::string& expected_type, 735 const std::string& path) { 736 std::string actual_type = GetJSONSchemaType(instance); 737 if (expected_type == actual_type || 738 (expected_type == schema::kNumber && actual_type == schema::kInteger)) { 739 return true; 740 } else if (expected_type == schema::kInteger && 741 actual_type == schema::kNumber) { 742 errors_.push_back(Error(path, kInvalidTypeIntegerNumber)); 743 return false; 744 } else { 745 errors_.push_back(Error(path, FormatErrorMessage( 746 kInvalidType, expected_type, actual_type))); 747 return false; 748 } 749} 750 751bool JSONSchemaValidator::SchemaAllowsAnyAdditionalItems( 752 const base::DictionaryValue* schema, 753 const base::DictionaryValue** additional_properties_schema) { 754 // If the validator allows additional properties globally, and this schema 755 // doesn't override, then we can exit early. 756 schema->GetDictionary(schema::kAdditionalProperties, 757 additional_properties_schema); 758 759 if (*additional_properties_schema) { 760 std::string additional_properties_type(schema::kAny); 761 CHECK((*additional_properties_schema)->GetString( 762 schema::kType, &additional_properties_type)); 763 return additional_properties_type == schema::kAny; 764 } else { 765 return default_allow_additional_properties_; 766 } 767} 768