• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
StorageSizespolicy::__anon3262da580111::StorageSizes48   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 
SchemaTypeToValueType(const std::string & type_string,base::Value::Type * type)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 
StrategyAllowInvalidOnTopLevel(SchemaOnErrorStrategy strategy)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 
StrategyAllowUnknownOnTopLevel(SchemaOnErrorStrategy strategy)94 bool StrategyAllowUnknownOnTopLevel(SchemaOnErrorStrategy strategy) {
95   return strategy != SCHEMA_STRICT;
96 }
97 
StrategyForNextLevel(SchemaOnErrorStrategy strategy)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 
SchemaErrorFound(std::string * error_path,std::string * error,const std::string & msg)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 
AddListIndexPrefixToPath(int index,std::string * path)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 
AddDictKeyPrefixToPath(const std::string & key,std::string * path)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 
data() const150   const SchemaData* data() const { return &schema_data_; }
151 
root_node() const152   const SchemaNode* root_node() const {
153     return schema(0);
154   }
155 
schema(int index) const156   const SchemaNode* schema(int index) const {
157     return schema_data_.schema_nodes + index;
158   }
159 
properties(int index) const160   const PropertiesNode* properties(int index) const {
161     return schema_data_.properties_nodes + index;
162   }
163 
property(int index) const164   const PropertyNode* property(int index) const {
165     return schema_data_.property_nodes + index;
166   }
167 
restriction(int index) const168   const RestrictionNode* restriction(int index) const {
169     return schema_data_.restriction_nodes + index;
170   }
171 
int_enums(int index) const172   const int* int_enums(int index) const {
173     return schema_data_.int_enums + index;
174   }
175 
string_enums(int index) const176   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 
InternalStorage()265 Schema::InternalStorage::InternalStorage()
266     : regex_cache_deleter_(&regex_cache_) {}
267 
~InternalStorage()268 Schema::InternalStorage::~InternalStorage() {}
269 
270 // static
Wrap(const SchemaData * data)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>
ParseSchema(const base::DictionaryValue & schema,std::string * error)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 
CompileRegex(const std::string & pattern) const343 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
DetermineStorageSizes(const base::DictionaryValue & schema,StorageSizes * sizes)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 
Parse(const base::DictionaryValue & schema,int * index,IdMap * id_map,ReferenceList * reference_list,std::string * error)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 
ParseDictionary(const base::DictionaryValue & schema,SchemaNode * schema_node,IdMap * id_map,ReferenceList * reference_list,std::string * error)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 
ParseList(const base::DictionaryValue & schema,SchemaNode * schema_node,IdMap * id_map,ReferenceList * reference_list,std::string * error)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 
ParseEnum(const base::DictionaryValue & schema,base::Value::Type type,SchemaNode * schema_node,std::string * error)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 
ParseRangedInt(const base::DictionaryValue & schema,SchemaNode * schema_node,std::string * error)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 
ParseStringPattern(const base::DictionaryValue & schema,SchemaNode * schema_node,std::string * error)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
ResolveReferences(const IdMap & id_map,const ReferenceList & reference_list,std::string * error)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 
Iterator(const scoped_refptr<const InternalStorage> & storage,const PropertiesNode * node)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 
Iterator(const Iterator & iterator)719 Schema::Iterator::Iterator(const Iterator& iterator)
720     : storage_(iterator.storage_),
721       it_(iterator.it_),
722       end_(iterator.end_) {}
723 
~Iterator()724 Schema::Iterator::~Iterator() {}
725 
operator =(const Iterator & iterator)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 
IsAtEnd() const733 bool Schema::Iterator::IsAtEnd() const {
734   return it_ == end_;
735 }
736 
Advance()737 void Schema::Iterator::Advance() {
738   ++it_;
739 }
740 
key() const741 const char* Schema::Iterator::key() const {
742   return it_->key;
743 }
744 
schema() const745 Schema Schema::Iterator::schema() const {
746   return Schema(storage_, storage_->schema(it_->schema));
747 }
748 
Schema()749 Schema::Schema() : node_(NULL) {}
750 
Schema(const scoped_refptr<const InternalStorage> & storage,const SchemaNode * node)751 Schema::Schema(const scoped_refptr<const InternalStorage>& storage,
752                const SchemaNode* node)
753     : storage_(storage), node_(node) {}
754 
Schema(const Schema & schema)755 Schema::Schema(const Schema& schema)
756     : storage_(schema.storage_), node_(schema.node_) {}
757 
~Schema()758 Schema::~Schema() {}
759 
operator =(const Schema & schema)760 Schema& Schema::operator=(const Schema& schema) {
761   storage_ = schema.storage_;
762   node_ = schema.node_;
763   return *this;
764 }
765 
766 // static
Wrap(const SchemaData * data)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 
Validate(const base::Value & value,SchemaOnErrorStrategy strategy,std::string * error_path,std::string * error) const772 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 
Normalize(base::Value * value,SchemaOnErrorStrategy strategy,std::string * error_path,std::string * error,bool * changed) const853 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
Parse(const std::string & content,std::string * error)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)
979     return Schema();
980   return Schema(storage, storage->root_node());
981 }
982 
type() const983 base::Value::Type Schema::type() const {
984   CHECK(valid());
985   return node_->type;
986 }
987 
GetPropertiesIterator() const988 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 
CompareKeys(const PropertyNode & node,const std::string & key)996 bool CompareKeys(const PropertyNode& node, const std::string& key) {
997   return node.key < key;
998 }
999 
1000 }  // namespace
1001 
GetKnownProperty(const std::string & key) const1002 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 
GetAdditionalProperties() const1014 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 
GetPatternProperties(const std::string & key) const1023 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 
GetProperty(const std::string & key) const1039 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 
GetMatchingProperties(const std::string & key) const1046 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 
GetItems() const1066 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 
ValidateIntegerRestriction(int index,int value) const1074 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 
ValidateStringRestriction(int index,const char * str) const1090 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