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/policy/core/common/schema.h" 6 7 #include <algorithm> 8 #include <climits> 9 #include <map> 10 #include <utility> 11 12 #include "base/compiler_specific.h" 13 #include "base/logging.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/memory/scoped_vector.h" 16 #include "base/stl_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "components/json_schema/json_schema_constants.h" 19 #include "components/json_schema/json_schema_validator.h" 20 #include "components/policy/core/common/schema_internal.h" 21 #include "third_party/re2/re2/re2.h" 22 23 namespace schema = json_schema_constants; 24 25 namespace policy { 26 27 using internal::PropertiesNode; 28 using internal::PropertyNode; 29 using internal::RestrictionNode; 30 using internal::SchemaData; 31 using internal::SchemaNode; 32 33 namespace { 34 35 // Maps schema "id" attributes to the corresponding SchemaNode index. 36 typedef std::map<std::string, int> IdMap; 37 38 // List of pairs of references to be assigned later. The string is the "id" 39 // whose corresponding index should be stored in the pointer, once all the IDs 40 // are available. 41 typedef std::vector<std::pair<std::string, int*> > ReferenceList; 42 43 // Sizes for the storage arrays. These are calculated in advance so that the 44 // arrays don't have to be resized during parsing, which would invalidate 45 // pointers into their contents (i.e. string's c_str() and address of indices 46 // for "$ref" attributes). 47 struct StorageSizes { 48 StorageSizes() 49 : strings(0), schema_nodes(0), property_nodes(0), properties_nodes(0), 50 restriction_nodes(0), int_enums(0), string_enums(0) { } 51 size_t strings; 52 size_t schema_nodes; 53 size_t property_nodes; 54 size_t properties_nodes; 55 size_t restriction_nodes; 56 size_t int_enums; 57 size_t string_enums; 58 }; 59 60 // An invalid index, indicating that a node is not present; similar to a NULL 61 // pointer. 62 const int kInvalid = -1; 63 64 bool SchemaTypeToValueType(const std::string& type_string, 65 base::Value::Type* type) { 66 // Note: "any" is not an accepted type. 67 static const struct { 68 const char* schema_type; 69 base::Value::Type value_type; 70 } kSchemaToValueTypeMap[] = { 71 { schema::kArray, base::Value::TYPE_LIST }, 72 { schema::kBoolean, base::Value::TYPE_BOOLEAN }, 73 { schema::kInteger, base::Value::TYPE_INTEGER }, 74 { schema::kNull, base::Value::TYPE_NULL }, 75 { schema::kNumber, base::Value::TYPE_DOUBLE }, 76 { schema::kObject, base::Value::TYPE_DICTIONARY }, 77 { schema::kString, base::Value::TYPE_STRING }, 78 }; 79 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { 80 if (kSchemaToValueTypeMap[i].schema_type == type_string) { 81 *type = kSchemaToValueTypeMap[i].value_type; 82 return true; 83 } 84 } 85 return false; 86 } 87 88 bool StrategyAllowInvalidOnTopLevel(SchemaOnErrorStrategy strategy) { 89 return strategy == SCHEMA_ALLOW_INVALID || 90 strategy == SCHEMA_ALLOW_INVALID_TOPLEVEL || 91 strategy == SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN; 92 } 93 94 bool StrategyAllowUnknownOnTopLevel(SchemaOnErrorStrategy strategy) { 95 return strategy != SCHEMA_STRICT; 96 } 97 98 SchemaOnErrorStrategy StrategyForNextLevel(SchemaOnErrorStrategy strategy) { 99 static SchemaOnErrorStrategy next_level_strategy[] = { 100 SCHEMA_STRICT, // SCHEMA_STRICT 101 SCHEMA_STRICT, // SCHEMA_ALLOW_UNKNOWN_TOPLEVEL 102 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_UNKNOWN 103 SCHEMA_STRICT, // SCHEMA_ALLOW_INVALID_TOPLEVEL 104 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN 105 SCHEMA_ALLOW_INVALID, // SCHEMA_ALLOW_INVALID 106 }; 107 return next_level_strategy[(int)strategy]; 108 } 109 110 void SchemaErrorFound(std::string* error_path, 111 std::string* error, 112 const std::string& msg) { 113 if (error_path) 114 *error_path = ""; 115 *error = msg; 116 } 117 118 void AddListIndexPrefixToPath(int index, std::string* path) { 119 if (path) { 120 if (path->empty()) 121 *path = base::StringPrintf("items[%d]", index); 122 else 123 *path = base::StringPrintf("items[%d].", index) + *path; 124 } 125 } 126 127 void AddDictKeyPrefixToPath(const std::string& key, std::string* path) { 128 if (path) { 129 if (path->empty()) 130 *path = key; 131 else 132 *path = key + "." + *path; 133 } 134 } 135 136 } // namespace 137 138 // Contains the internal data representation of a Schema. This can either wrap 139 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which 140 // is generated at compile time), or it can own its own SchemaData. 141 class Schema::InternalStorage 142 : public base::RefCountedThreadSafe<InternalStorage> { 143 public: 144 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); 145 146 static scoped_refptr<const InternalStorage> ParseSchema( 147 const base::DictionaryValue& schema, 148 std::string* error); 149 150 const SchemaData* data() const { return &schema_data_; } 151 152 const SchemaNode* root_node() const { 153 return schema(0); 154 } 155 156 const SchemaNode* schema(int index) const { 157 return schema_data_.schema_nodes + index; 158 } 159 160 const PropertiesNode* properties(int index) const { 161 return schema_data_.properties_nodes + index; 162 } 163 164 const PropertyNode* property(int index) const { 165 return schema_data_.property_nodes + index; 166 } 167 168 const RestrictionNode* restriction(int index) const { 169 return schema_data_.restriction_nodes + index; 170 } 171 172 const int* int_enums(int index) const { 173 return schema_data_.int_enums + index; 174 } 175 176 const char** string_enums(int index) const { 177 return schema_data_.string_enums + index; 178 } 179 180 // Compiles regular expression |pattern|. The result is cached and will be 181 // returned directly next time. 182 re2::RE2* CompileRegex(const std::string& pattern) const; 183 184 private: 185 friend class base::RefCountedThreadSafe<InternalStorage>; 186 187 InternalStorage(); 188 ~InternalStorage(); 189 190 // Determines the expected |sizes| of the storage for the representation 191 // of |schema|. 192 static void DetermineStorageSizes(const base::DictionaryValue& schema, 193 StorageSizes* sizes); 194 195 // Parses the JSON schema in |schema|. 196 // 197 // If |schema| has a "$ref" attribute then a pending reference is appended 198 // to the |reference_list|, and nothing else is done. 199 // 200 // Otherwise, |index| gets assigned the index of the corresponding SchemaNode 201 // in |schema_nodes_|. If the |schema| contains an "id" then that ID is mapped 202 // to the |index| in the |id_map|. 203 // 204 // If |schema| is invalid then |error| gets the error reason and false is 205 // returned. Otherwise returns true. 206 bool Parse(const base::DictionaryValue& schema, 207 int* index, 208 IdMap* id_map, 209 ReferenceList* reference_list, 210 std::string* error); 211 212 // Helper for Parse() that gets an already assigned |schema_node| instead of 213 // an |index| pointer. 214 bool ParseDictionary(const base::DictionaryValue& schema, 215 SchemaNode* schema_node, 216 IdMap* id_map, 217 ReferenceList* reference_list, 218 std::string* error); 219 220 // Helper for Parse() that gets an already assigned |schema_node| instead of 221 // an |index| pointer. 222 bool ParseList(const base::DictionaryValue& schema, 223 SchemaNode* schema_node, 224 IdMap* id_map, 225 ReferenceList* reference_list, 226 std::string* error); 227 228 bool ParseEnum(const base::DictionaryValue& schema, 229 base::Value::Type type, 230 SchemaNode* schema_node, 231 std::string* error); 232 233 bool ParseRangedInt(const base::DictionaryValue& schema, 234 SchemaNode* schema_node, 235 std::string* error); 236 237 bool ParseStringPattern(const base::DictionaryValue& schema, 238 SchemaNode* schema_node, 239 std::string* error); 240 241 // Assigns the IDs in |id_map| to the pending references in the 242 // |reference_list|. If an ID is missing then |error| is set and false is 243 // returned; otherwise returns true. 244 static bool ResolveReferences(const IdMap& id_map, 245 const ReferenceList& reference_list, 246 std::string* error); 247 248 // Cache for CompileRegex(), will memorize return value of every call to 249 // CompileRegex() and return results directly next time. 250 mutable std::map<std::string, re2::RE2*> regex_cache_; 251 STLValueDeleter<std::map<std::string, re2::RE2*> > regex_cache_deleter_; 252 253 SchemaData schema_data_; 254 std::vector<std::string> strings_; 255 std::vector<SchemaNode> schema_nodes_; 256 std::vector<PropertyNode> property_nodes_; 257 std::vector<PropertiesNode> properties_nodes_; 258 std::vector<RestrictionNode> restriction_nodes_; 259 std::vector<int> int_enums_; 260 std::vector<const char*> string_enums_; 261 262 DISALLOW_COPY_AND_ASSIGN(InternalStorage); 263 }; 264 265 Schema::InternalStorage::InternalStorage() 266 : regex_cache_deleter_(®ex_cache_) {} 267 268 Schema::InternalStorage::~InternalStorage() {} 269 270 // static 271 scoped_refptr<const Schema::InternalStorage> Schema::InternalStorage::Wrap( 272 const SchemaData* data) { 273 InternalStorage* storage = new InternalStorage(); 274 storage->schema_data_.schema_nodes = data->schema_nodes; 275 storage->schema_data_.property_nodes = data->property_nodes; 276 storage->schema_data_.properties_nodes = data->properties_nodes; 277 storage->schema_data_.restriction_nodes = data->restriction_nodes; 278 storage->schema_data_.int_enums = data->int_enums; 279 storage->schema_data_.string_enums = data->string_enums; 280 return storage; 281 } 282 283 // static 284 scoped_refptr<const Schema::InternalStorage> 285 Schema::InternalStorage::ParseSchema(const base::DictionaryValue& schema, 286 std::string* error) { 287 // Determine the sizes of the storage arrays and reserve the capacity before 288 // starting to append nodes and strings. This is important to prevent the 289 // arrays from being reallocated, which would invalidate the c_str() pointers 290 // and the addresses of indices to fix. 291 StorageSizes sizes; 292 DetermineStorageSizes(schema, &sizes); 293 294 scoped_refptr<InternalStorage> storage = new InternalStorage(); 295 storage->strings_.reserve(sizes.strings); 296 storage->schema_nodes_.reserve(sizes.schema_nodes); 297 storage->property_nodes_.reserve(sizes.property_nodes); 298 storage->properties_nodes_.reserve(sizes.properties_nodes); 299 storage->restriction_nodes_.reserve(sizes.restriction_nodes); 300 storage->int_enums_.reserve(sizes.int_enums); 301 storage->string_enums_.reserve(sizes.string_enums); 302 303 int root_index = kInvalid; 304 IdMap id_map; 305 ReferenceList reference_list; 306 if (!storage->Parse(schema, &root_index, &id_map, &reference_list, error)) 307 return NULL; 308 309 if (root_index == kInvalid) { 310 *error = "The main schema can't have a $ref"; 311 return NULL; 312 } 313 314 // None of this should ever happen without having been already detected. 315 // But, if it does happen, then it will lead to corrupted memory; drop 316 // everything in that case. 317 if (root_index != 0 || 318 sizes.strings != storage->strings_.size() || 319 sizes.schema_nodes != storage->schema_nodes_.size() || 320 sizes.property_nodes != storage->property_nodes_.size() || 321 sizes.properties_nodes != storage->properties_nodes_.size() || 322 sizes.restriction_nodes != storage->restriction_nodes_.size() || 323 sizes.int_enums != storage->int_enums_.size() || 324 sizes.string_enums != storage->string_enums_.size()) { 325 *error = "Failed to parse the schema due to a Chrome bug. Please file a " 326 "new issue at http://crbug.com"; 327 return NULL; 328 } 329 330 if (!ResolveReferences(id_map, reference_list, error)) 331 return NULL; 332 333 SchemaData* data = &storage->schema_data_; 334 data->schema_nodes = vector_as_array(&storage->schema_nodes_); 335 data->property_nodes = vector_as_array(&storage->property_nodes_); 336 data->properties_nodes = vector_as_array(&storage->properties_nodes_); 337 data->restriction_nodes = vector_as_array(&storage->restriction_nodes_); 338 data->int_enums = vector_as_array(&storage->int_enums_); 339 data->string_enums = vector_as_array(&storage->string_enums_); 340 return storage; 341 } 342 343 re2::RE2* Schema::InternalStorage::CompileRegex( 344 const std::string& pattern) const { 345 std::map<std::string, re2::RE2*>::iterator it = regex_cache_.find(pattern); 346 if (it == regex_cache_.end()) { 347 re2::RE2* compiled = new re2::RE2(pattern); 348 regex_cache_[pattern] = compiled; 349 return compiled; 350 } 351 return it->second; 352 } 353 354 // static 355 void Schema::InternalStorage::DetermineStorageSizes( 356 const base::DictionaryValue& schema, 357 StorageSizes* sizes) { 358 std::string ref_string; 359 if (schema.GetString(schema::kRef, &ref_string)) { 360 // Schemas with a "$ref" attribute don't take additional storage. 361 return; 362 } 363 364 std::string type_string; 365 base::Value::Type type = base::Value::TYPE_NULL; 366 if (!schema.GetString(schema::kType, &type_string) || 367 !SchemaTypeToValueType(type_string, &type)) { 368 // This schema is invalid. 369 return; 370 } 371 372 sizes->schema_nodes++; 373 374 if (type == base::Value::TYPE_LIST) { 375 const base::DictionaryValue* items = NULL; 376 if (schema.GetDictionary(schema::kItems, &items)) 377 DetermineStorageSizes(*items, sizes); 378 } else if (type == base::Value::TYPE_DICTIONARY) { 379 sizes->properties_nodes++; 380 381 const base::DictionaryValue* dict = NULL; 382 if (schema.GetDictionary(schema::kAdditionalProperties, &dict)) 383 DetermineStorageSizes(*dict, sizes); 384 385 const base::DictionaryValue* properties = NULL; 386 if (schema.GetDictionary(schema::kProperties, &properties)) { 387 for (base::DictionaryValue::Iterator it(*properties); 388 !it.IsAtEnd(); it.Advance()) { 389 // This should have been verified by the JSONSchemaValidator. 390 CHECK(it.value().GetAsDictionary(&dict)); 391 DetermineStorageSizes(*dict, sizes); 392 sizes->strings++; 393 sizes->property_nodes++; 394 } 395 } 396 397 const base::DictionaryValue* pattern_properties = NULL; 398 if (schema.GetDictionary(schema::kPatternProperties, &pattern_properties)) { 399 for (base::DictionaryValue::Iterator it(*pattern_properties); 400 !it.IsAtEnd(); it.Advance()) { 401 CHECK(it.value().GetAsDictionary(&dict)); 402 DetermineStorageSizes(*dict, sizes); 403 sizes->strings++; 404 sizes->property_nodes++; 405 } 406 } 407 } else if (schema.HasKey(schema::kEnum)) { 408 const base::ListValue* possible_values = NULL; 409 if (schema.GetList(schema::kEnum, &possible_values)) { 410 if (type == base::Value::TYPE_INTEGER) { 411 sizes->int_enums += possible_values->GetSize(); 412 } else if (type == base::Value::TYPE_STRING) { 413 sizes->string_enums += possible_values->GetSize(); 414 sizes->strings += possible_values->GetSize(); 415 } 416 sizes->restriction_nodes++; 417 } 418 } else if (type == base::Value::TYPE_INTEGER) { 419 if (schema.HasKey(schema::kMinimum) || schema.HasKey(schema::kMaximum)) 420 sizes->restriction_nodes++; 421 } else if (type == base::Value::TYPE_STRING) { 422 if (schema.HasKey(schema::kPattern)) { 423 sizes->strings++; 424 sizes->string_enums++; 425 sizes->restriction_nodes++; 426 } 427 } 428 } 429 430 bool Schema::InternalStorage::Parse(const base::DictionaryValue& schema, 431 int* index, 432 IdMap* id_map, 433 ReferenceList* reference_list, 434 std::string* error) { 435 std::string ref_string; 436 if (schema.GetString(schema::kRef, &ref_string)) { 437 std::string id_string; 438 if (schema.GetString(schema::kId, &id_string)) { 439 *error = "Schemas with a $ref can't have an id"; 440 return false; 441 } 442 reference_list->push_back(std::make_pair(ref_string, index)); 443 return true; 444 } 445 446 std::string type_string; 447 if (!schema.GetString(schema::kType, &type_string)) { 448 *error = "The schema type must be declared."; 449 return false; 450 } 451 452 base::Value::Type type = base::Value::TYPE_NULL; 453 if (!SchemaTypeToValueType(type_string, &type)) { 454 *error = "Type not supported: " + type_string; 455 return false; 456 } 457 458 *index = static_cast<int>(schema_nodes_.size()); 459 schema_nodes_.push_back(SchemaNode()); 460 SchemaNode* schema_node = &schema_nodes_.back(); 461 schema_node->type = type; 462 schema_node->extra = kInvalid; 463 464 if (type == base::Value::TYPE_DICTIONARY) { 465 if (!ParseDictionary(schema, schema_node, id_map, reference_list, error)) 466 return false; 467 } else if (type == base::Value::TYPE_LIST) { 468 if (!ParseList(schema, schema_node, id_map, reference_list, error)) 469 return false; 470 } else if (schema.HasKey(schema::kEnum)) { 471 if (!ParseEnum(schema, type, schema_node, error)) 472 return false; 473 } else if (schema.HasKey(schema::kPattern)) { 474 if (!ParseStringPattern(schema, schema_node, error)) 475 return false; 476 } else if (schema.HasKey(schema::kMinimum) || 477 schema.HasKey(schema::kMaximum)) { 478 if (type != base::Value::TYPE_INTEGER) { 479 *error = "Only integers can have minimum and maximum"; 480 return false; 481 } 482 if (!ParseRangedInt(schema, schema_node, error)) 483 return false; 484 } 485 std::string id_string; 486 if (schema.GetString(schema::kId, &id_string)) { 487 if (ContainsKey(*id_map, id_string)) { 488 *error = "Duplicated id: " + id_string; 489 return false; 490 } 491 (*id_map)[id_string] = *index; 492 } 493 494 return true; 495 } 496 497 bool Schema::InternalStorage::ParseDictionary( 498 const base::DictionaryValue& schema, 499 SchemaNode* schema_node, 500 IdMap* id_map, 501 ReferenceList* reference_list, 502 std::string* error) { 503 int extra = static_cast<int>(properties_nodes_.size()); 504 properties_nodes_.push_back(PropertiesNode()); 505 properties_nodes_[extra].additional = kInvalid; 506 schema_node->extra = extra; 507 508 const base::DictionaryValue* dict = NULL; 509 if (schema.GetDictionary(schema::kAdditionalProperties, &dict)) { 510 if (!Parse(*dict, &properties_nodes_[extra].additional, 511 id_map, reference_list, error)) { 512 return false; 513 } 514 } 515 516 properties_nodes_[extra].begin = static_cast<int>(property_nodes_.size()); 517 518 const base::DictionaryValue* properties = NULL; 519 if (schema.GetDictionary(schema::kProperties, &properties)) { 520 // This and below reserves nodes for all of the |properties|, and makes sure 521 // they are contiguous. Recursive calls to Parse() will append after these 522 // elements. 523 property_nodes_.resize(property_nodes_.size() + properties->size()); 524 } 525 526 properties_nodes_[extra].end = static_cast<int>(property_nodes_.size()); 527 528 const base::DictionaryValue* pattern_properties = NULL; 529 if (schema.GetDictionary(schema::kPatternProperties, &pattern_properties)) 530 property_nodes_.resize(property_nodes_.size() + pattern_properties->size()); 531 532 properties_nodes_[extra].pattern_end = 533 static_cast<int>(property_nodes_.size()); 534 535 if (properties != NULL) { 536 int base_index = properties_nodes_[extra].begin; 537 int index = base_index; 538 539 for (base::DictionaryValue::Iterator it(*properties); 540 !it.IsAtEnd(); it.Advance(), ++index) { 541 // This should have been verified by the JSONSchemaValidator. 542 CHECK(it.value().GetAsDictionary(&dict)); 543 strings_.push_back(it.key()); 544 property_nodes_[index].key = strings_.back().c_str(); 545 if (!Parse(*dict, &property_nodes_[index].schema, 546 id_map, reference_list, error)) { 547 return false; 548 } 549 } 550 CHECK_EQ(static_cast<int>(properties->size()), index - base_index); 551 } 552 553 if (pattern_properties != NULL) { 554 int base_index = properties_nodes_[extra].end; 555 int index = base_index; 556 557 for (base::DictionaryValue::Iterator it(*pattern_properties); 558 !it.IsAtEnd(); it.Advance(), ++index) { 559 CHECK(it.value().GetAsDictionary(&dict)); 560 re2::RE2* compiled_regex = CompileRegex(it.key()); 561 if (!compiled_regex->ok()) { 562 *error = 563 "/" + it.key() + "/ is a invalid regex: " + compiled_regex->error(); 564 return false; 565 } 566 strings_.push_back(it.key()); 567 property_nodes_[index].key = strings_.back().c_str(); 568 if (!Parse(*dict, &property_nodes_[index].schema, 569 id_map, reference_list, error)) { 570 return false; 571 } 572 } 573 CHECK_EQ(static_cast<int>(pattern_properties->size()), index - base_index); 574 } 575 576 if (properties_nodes_[extra].begin == properties_nodes_[extra].pattern_end) { 577 properties_nodes_[extra].begin = kInvalid; 578 properties_nodes_[extra].end = kInvalid; 579 properties_nodes_[extra].pattern_end = kInvalid; 580 } 581 582 return true; 583 } 584 585 bool Schema::InternalStorage::ParseList(const base::DictionaryValue& schema, 586 SchemaNode* schema_node, 587 IdMap* id_map, 588 ReferenceList* reference_list, 589 std::string* error) { 590 const base::DictionaryValue* dict = NULL; 591 if (!schema.GetDictionary(schema::kItems, &dict)) { 592 *error = "Arrays must declare a single schema for their items."; 593 return false; 594 } 595 return Parse(*dict, &schema_node->extra, id_map, reference_list, error); 596 } 597 598 bool Schema::InternalStorage::ParseEnum(const base::DictionaryValue& schema, 599 base::Value::Type type, 600 SchemaNode* schema_node, 601 std::string* error) { 602 const base::ListValue *possible_values = NULL; 603 if (!schema.GetList(schema::kEnum, &possible_values)) { 604 *error = "Enum attribute must be a list value"; 605 return false; 606 } 607 if (possible_values->empty()) { 608 *error = "Enum attribute must be non-empty"; 609 return false; 610 } 611 int offset_begin; 612 int offset_end; 613 if (type == base::Value::TYPE_INTEGER) { 614 offset_begin = static_cast<int>(int_enums_.size()); 615 int value; 616 for (base::ListValue::const_iterator it = possible_values->begin(); 617 it != possible_values->end(); ++it) { 618 if (!(*it)->GetAsInteger(&value)) { 619 *error = "Invalid enumeration member type"; 620 return false; 621 } 622 int_enums_.push_back(value); 623 } 624 offset_end = static_cast<int>(int_enums_.size()); 625 } else if (type == base::Value::TYPE_STRING) { 626 offset_begin = static_cast<int>(string_enums_.size()); 627 std::string value; 628 for (base::ListValue::const_iterator it = possible_values->begin(); 629 it != possible_values->end(); ++it) { 630 if (!(*it)->GetAsString(&value)) { 631 *error = "Invalid enumeration member type"; 632 return false; 633 } 634 strings_.push_back(value); 635 string_enums_.push_back(strings_.back().c_str()); 636 } 637 offset_end = static_cast<int>(string_enums_.size()); 638 } else { 639 *error = "Enumeration is only supported for integer and string."; 640 return false; 641 } 642 schema_node->extra = static_cast<int>(restriction_nodes_.size()); 643 restriction_nodes_.push_back(RestrictionNode()); 644 restriction_nodes_.back().enumeration_restriction.offset_begin = offset_begin; 645 restriction_nodes_.back().enumeration_restriction.offset_end = offset_end; 646 return true; 647 } 648 649 bool Schema::InternalStorage::ParseRangedInt( 650 const base::DictionaryValue& schema, 651 SchemaNode* schema_node, 652 std::string* error) { 653 int min_value = INT_MIN; 654 int max_value = INT_MAX; 655 int value; 656 if (schema.GetInteger(schema::kMinimum, &value)) 657 min_value = value; 658 if (schema.GetInteger(schema::kMaximum, &value)) 659 max_value = value; 660 if (min_value > max_value) { 661 *error = "Invalid range restriction for int type."; 662 return false; 663 } 664 schema_node->extra = static_cast<int>(restriction_nodes_.size()); 665 restriction_nodes_.push_back(RestrictionNode()); 666 restriction_nodes_.back().ranged_restriction.max_value = max_value; 667 restriction_nodes_.back().ranged_restriction.min_value = min_value; 668 return true; 669 } 670 671 bool Schema::InternalStorage::ParseStringPattern( 672 const base::DictionaryValue& schema, 673 SchemaNode* schema_node, 674 std::string* error) { 675 std::string pattern; 676 if (!schema.GetString(schema::kPattern, &pattern)) { 677 *error = "Schema pattern must be a string."; 678 return false; 679 } 680 re2::RE2* compiled_regex = CompileRegex(pattern); 681 if (!compiled_regex->ok()) { 682 *error = "/" + pattern + "/ is invalid regex: " + compiled_regex->error(); 683 return false; 684 } 685 int index = static_cast<int>(string_enums_.size()); 686 strings_.push_back(pattern); 687 string_enums_.push_back(strings_.back().c_str()); 688 schema_node->extra = static_cast<int>(restriction_nodes_.size()); 689 restriction_nodes_.push_back(RestrictionNode()); 690 restriction_nodes_.back().string_pattern_restriction.pattern_index = index; 691 restriction_nodes_.back().string_pattern_restriction.pattern_index_backup = 692 index; 693 return true; 694 } 695 696 // static 697 bool Schema::InternalStorage::ResolveReferences( 698 const IdMap& id_map, 699 const ReferenceList& reference_list, 700 std::string* error) { 701 for (ReferenceList::const_iterator ref = reference_list.begin(); 702 ref != reference_list.end(); ++ref) { 703 IdMap::const_iterator id = id_map.find(ref->first); 704 if (id == id_map.end()) { 705 *error = "Invalid $ref: " + ref->first; 706 return false; 707 } 708 *ref->second = id->second; 709 } 710 return true; 711 } 712 713 Schema::Iterator::Iterator(const scoped_refptr<const InternalStorage>& storage, 714 const PropertiesNode* node) 715 : storage_(storage), 716 it_(storage->property(node->begin)), 717 end_(storage->property(node->end)) {} 718 719 Schema::Iterator::Iterator(const Iterator& iterator) 720 : storage_(iterator.storage_), 721 it_(iterator.it_), 722 end_(iterator.end_) {} 723 724 Schema::Iterator::~Iterator() {} 725 726 Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) { 727 storage_ = iterator.storage_; 728 it_ = iterator.it_; 729 end_ = iterator.end_; 730 return *this; 731 } 732 733 bool Schema::Iterator::IsAtEnd() const { 734 return it_ == end_; 735 } 736 737 void Schema::Iterator::Advance() { 738 ++it_; 739 } 740 741 const char* Schema::Iterator::key() const { 742 return it_->key; 743 } 744 745 Schema Schema::Iterator::schema() const { 746 return Schema(storage_, storage_->schema(it_->schema)); 747 } 748 749 Schema::Schema() : node_(NULL) {} 750 751 Schema::Schema(const scoped_refptr<const InternalStorage>& storage, 752 const SchemaNode* node) 753 : storage_(storage), node_(node) {} 754 755 Schema::Schema(const Schema& schema) 756 : storage_(schema.storage_), node_(schema.node_) {} 757 758 Schema::~Schema() {} 759 760 Schema& Schema::operator=(const Schema& schema) { 761 storage_ = schema.storage_; 762 node_ = schema.node_; 763 return *this; 764 } 765 766 // static 767 Schema Schema::Wrap(const SchemaData* data) { 768 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); 769 return Schema(storage, storage->root_node()); 770 } 771 772 bool Schema::Validate(const base::Value& value, 773 SchemaOnErrorStrategy strategy, 774 std::string* error_path, 775 std::string* error) const { 776 if (!valid()) { 777 SchemaErrorFound(error_path, error, "The schema is invalid."); 778 return false; 779 } 780 781 if (!value.IsType(type())) { 782 // Allow the integer to double promotion. Note that range restriction on 783 // double is not supported now. 784 if (value.IsType(base::Value::TYPE_INTEGER) && 785 type() == base::Value::TYPE_DOUBLE) { 786 return true; 787 } 788 789 SchemaErrorFound( 790 error_path, error, "The value type doesn't match the schema type."); 791 return false; 792 } 793 794 const base::DictionaryValue* dict = NULL; 795 const base::ListValue* list = NULL; 796 int int_value; 797 std::string str_value; 798 if (value.GetAsDictionary(&dict)) { 799 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); 800 it.Advance()) { 801 SchemaList schema_list = GetMatchingProperties(it.key()); 802 if (schema_list.empty()) { 803 // Unknown property was detected. 804 SchemaErrorFound(error_path, error, "Unknown property: " + it.key()); 805 if (!StrategyAllowUnknownOnTopLevel(strategy)) 806 return false; 807 } else { 808 for (SchemaList::iterator subschema = schema_list.begin(); 809 subschema != schema_list.end(); ++subschema) { 810 if (!subschema->Validate(it.value(), 811 StrategyForNextLevel(strategy), 812 error_path, 813 error)) { 814 // Invalid property was detected. 815 AddDictKeyPrefixToPath(it.key(), error_path); 816 if (!StrategyAllowInvalidOnTopLevel(strategy)) 817 return false; 818 } 819 } 820 } 821 } 822 } else if (value.GetAsList(&list)) { 823 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); 824 ++it) { 825 if (!*it || 826 !GetItems().Validate(**it, 827 StrategyForNextLevel(strategy), 828 error_path, 829 error)) { 830 // Invalid list item was detected. 831 AddListIndexPrefixToPath(it - list->begin(), error_path); 832 if (!StrategyAllowInvalidOnTopLevel(strategy)) 833 return false; 834 } 835 } 836 } else if (value.GetAsInteger(&int_value)) { 837 if (node_->extra != kInvalid && 838 !ValidateIntegerRestriction(node_->extra, int_value)) { 839 SchemaErrorFound(error_path, error, "Invalid value for integer"); 840 return false; 841 } 842 } else if (value.GetAsString(&str_value)) { 843 if (node_->extra != kInvalid && 844 !ValidateStringRestriction(node_->extra, str_value.c_str())) { 845 SchemaErrorFound(error_path, error, "Invalid value for string"); 846 return false; 847 } 848 } 849 850 return true; 851 } 852 853 bool Schema::Normalize(base::Value* value, 854 SchemaOnErrorStrategy strategy, 855 std::string* error_path, 856 std::string* error, 857 bool* changed) const { 858 if (!valid()) { 859 SchemaErrorFound(error_path, error, "The schema is invalid."); 860 return false; 861 } 862 863 if (!value->IsType(type())) { 864 // Allow the integer to double promotion. Note that range restriction on 865 // double is not supported now. 866 if (value->IsType(base::Value::TYPE_INTEGER) && 867 type() == base::Value::TYPE_DOUBLE) { 868 return true; 869 } 870 871 SchemaErrorFound( 872 error_path, error, "The value type doesn't match the schema type."); 873 return false; 874 } 875 876 base::DictionaryValue* dict = NULL; 877 base::ListValue* list = NULL; 878 if (value->GetAsDictionary(&dict)) { 879 std::vector<std::string> drop_list; // Contains the keys to drop. 880 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); 881 it.Advance()) { 882 SchemaList schema_list = GetMatchingProperties(it.key()); 883 if (schema_list.empty()) { 884 // Unknown property was detected. 885 SchemaErrorFound(error_path, error, "Unknown property: " + it.key()); 886 if (StrategyAllowUnknownOnTopLevel(strategy)) 887 drop_list.push_back(it.key()); 888 else 889 return false; 890 } else { 891 for (SchemaList::iterator subschema = schema_list.begin(); 892 subschema != schema_list.end(); ++subschema) { 893 base::Value* sub_value = NULL; 894 dict->GetWithoutPathExpansion(it.key(), &sub_value); 895 if (!subschema->Normalize(sub_value, 896 StrategyForNextLevel(strategy), 897 error_path, 898 error, 899 changed)) { 900 // Invalid property was detected. 901 AddDictKeyPrefixToPath(it.key(), error_path); 902 if (StrategyAllowInvalidOnTopLevel(strategy)) { 903 drop_list.push_back(it.key()); 904 break; 905 } else { 906 return false; 907 } 908 } 909 } 910 } 911 } 912 if (changed && !drop_list.empty()) 913 *changed = true; 914 for (std::vector<std::string>::const_iterator it = drop_list.begin(); 915 it != drop_list.end(); 916 ++it) { 917 dict->RemoveWithoutPathExpansion(*it, NULL); 918 } 919 return true; 920 } else if (value->GetAsList(&list)) { 921 std::vector<size_t> drop_list; // Contains the indexes to drop. 922 for (size_t index = 0; index < list->GetSize(); index++) { 923 base::Value* sub_value = NULL; 924 list->Get(index, &sub_value); 925 if (!sub_value || !GetItems().Normalize(sub_value, 926 StrategyForNextLevel(strategy), 927 error_path, 928 error, 929 changed)) { 930 // Invalid list item was detected. 931 AddListIndexPrefixToPath(index, error_path); 932 if (StrategyAllowInvalidOnTopLevel(strategy)) 933 drop_list.push_back(index); 934 else 935 return false; 936 } 937 } 938 if (changed && !drop_list.empty()) 939 *changed = true; 940 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); 941 it != drop_list.rend(); ++it) { 942 list->Remove(*it, NULL); 943 } 944 return true; 945 } 946 947 return Validate(*value, strategy, error_path, error); 948 } 949 950 // static 951 Schema Schema::Parse(const std::string& content, std::string* error) { 952 // Validate as a generic JSON schema, and ignore unknown attributes; they 953 // may become used in a future version of the schema format. 954 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( 955 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); 956 if (!dict) 957 return Schema(); 958 959 // Validate the main type. 960 std::string string_value; 961 if (!dict->GetString(schema::kType, &string_value) || 962 string_value != schema::kObject) { 963 *error = 964 "The main schema must have a type attribute with \"object\" value."; 965 return Schema(); 966 } 967 968 // Checks for invalid attributes at the top-level. 969 if (dict->HasKey(schema::kAdditionalProperties) || 970 dict->HasKey(schema::kPatternProperties)) { 971 *error = "\"additionalProperties\" and \"patternProperties\" are not " 972 "supported at the main schema."; 973 return Schema(); 974 } 975 976 scoped_refptr<const InternalStorage> storage = 977 InternalStorage::ParseSchema(*dict, error); 978 if (!storage.get()) 979 return Schema(); 980 return Schema(storage, storage->root_node()); 981 } 982 983 base::Value::Type Schema::type() const { 984 CHECK(valid()); 985 return node_->type; 986 } 987 988 Schema::Iterator Schema::GetPropertiesIterator() const { 989 CHECK(valid()); 990 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 991 return Iterator(storage_, storage_->properties(node_->extra)); 992 } 993 994 namespace { 995 996 bool CompareKeys(const PropertyNode& node, const std::string& key) { 997 return node.key < key; 998 } 999 1000 } // namespace 1001 1002 Schema Schema::GetKnownProperty(const std::string& key) const { 1003 CHECK(valid()); 1004 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 1005 const PropertiesNode* node = storage_->properties(node_->extra); 1006 const PropertyNode* begin = storage_->property(node->begin); 1007 const PropertyNode* end = storage_->property(node->end); 1008 const PropertyNode* it = std::lower_bound(begin, end, key, CompareKeys); 1009 if (it != end && it->key == key) 1010 return Schema(storage_, storage_->schema(it->schema)); 1011 return Schema(); 1012 } 1013 1014 Schema Schema::GetAdditionalProperties() const { 1015 CHECK(valid()); 1016 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 1017 const PropertiesNode* node = storage_->properties(node_->extra); 1018 if (node->additional == kInvalid) 1019 return Schema(); 1020 return Schema(storage_, storage_->schema(node->additional)); 1021 } 1022 1023 SchemaList Schema::GetPatternProperties(const std::string& key) const { 1024 CHECK(valid()); 1025 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 1026 const PropertiesNode* node = storage_->properties(node_->extra); 1027 const PropertyNode* begin = storage_->property(node->end); 1028 const PropertyNode* end = storage_->property(node->pattern_end); 1029 SchemaList matching_properties; 1030 for (const PropertyNode* it = begin; it != end; ++it) { 1031 if (re2::RE2::PartialMatch(key, *storage_->CompileRegex(it->key))) { 1032 matching_properties.push_back( 1033 Schema(storage_, storage_->schema(it->schema))); 1034 } 1035 } 1036 return matching_properties; 1037 } 1038 1039 Schema Schema::GetProperty(const std::string& key) const { 1040 Schema schema = GetKnownProperty(key); 1041 if (schema.valid()) 1042 return schema; 1043 return GetAdditionalProperties(); 1044 } 1045 1046 SchemaList Schema::GetMatchingProperties(const std::string& key) const { 1047 SchemaList schema_list; 1048 1049 Schema known_property = GetKnownProperty(key); 1050 if (known_property.valid()) 1051 schema_list.push_back(known_property); 1052 1053 SchemaList pattern_properties = GetPatternProperties(key); 1054 schema_list.insert( 1055 schema_list.end(), pattern_properties.begin(), pattern_properties.end()); 1056 1057 if (schema_list.empty()) { 1058 Schema additional_property = GetAdditionalProperties(); 1059 if (additional_property.valid()) 1060 schema_list.push_back(additional_property); 1061 } 1062 1063 return schema_list; 1064 } 1065 1066 Schema Schema::GetItems() const { 1067 CHECK(valid()); 1068 CHECK_EQ(base::Value::TYPE_LIST, type()); 1069 if (node_->extra == kInvalid) 1070 return Schema(); 1071 return Schema(storage_, storage_->schema(node_->extra)); 1072 } 1073 1074 bool Schema::ValidateIntegerRestriction(int index, int value) const { 1075 const RestrictionNode* rnode = storage_->restriction(index); 1076 if (rnode->ranged_restriction.min_value <= 1077 rnode->ranged_restriction.max_value) { 1078 return rnode->ranged_restriction.min_value <= value && 1079 rnode->ranged_restriction.max_value >= value; 1080 } else { 1081 for (int i = rnode->enumeration_restriction.offset_begin; 1082 i < rnode->enumeration_restriction.offset_end; ++i) { 1083 if (*storage_->int_enums(i) == value) 1084 return true; 1085 } 1086 return false; 1087 } 1088 } 1089 1090 bool Schema::ValidateStringRestriction(int index, const char* str) const { 1091 const RestrictionNode* rnode = storage_->restriction(index); 1092 if (rnode->enumeration_restriction.offset_begin < 1093 rnode->enumeration_restriction.offset_end) { 1094 for (int i = rnode->enumeration_restriction.offset_begin; 1095 i < rnode->enumeration_restriction.offset_end; ++i) { 1096 if (strcmp(*storage_->string_enums(i), str) == 0) 1097 return true; 1098 } 1099 return false; 1100 } else { 1101 int index = rnode->string_pattern_restriction.pattern_index; 1102 DCHECK(index == rnode->string_pattern_restriction.pattern_index_backup); 1103 re2::RE2* regex = storage_->CompileRegex(*storage_->string_enums(index)); 1104 return re2::RE2::PartialMatch(str, *regex); 1105 } 1106 } 1107 1108 } // namespace policy 1109