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