From 1e9d98078088f3adc80933bcd81a915a3f75e14b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 21 Feb 2021 14:21:33 +1300 Subject: [PATCH] Fix validating duplicate key in dependencies and dependentSchemas (#241) --- .../JSchemaValidatingReaderTests.cs | 94 +++++++++++++++++++ .../Infrastructure/Validation/ObjectScope.cs | 20 +++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/Src/Newtonsoft.Json.Schema.Tests/JSchemaValidatingReaderTests.cs b/Src/Newtonsoft.Json.Schema.Tests/JSchemaValidatingReaderTests.cs index 070b756d..88b06568 100644 --- a/Src/Newtonsoft.Json.Schema.Tests/JSchemaValidatingReaderTests.cs +++ b/Src/Newtonsoft.Json.Schema.Tests/JSchemaValidatingReaderTests.cs @@ -3500,6 +3500,100 @@ public void ValidateDynamicRef() Assert.AreEqual(1, validationEventArgs.ValidationError.ChildErrors[0].ChildErrors.Count); Assert.AreEqual("Invalid type. Expected Integer, Object but got Number.", validationEventArgs.ValidationError.ChildErrors[0].ChildErrors[0].Message); } + + [Test] + public void Read_KeyDuplicatedInDependenciesAndDependentSchemas_IgnoreSecond() + { + string json = @"{ + ""$schema"": ""http://json-schema.org/draft-07/schema"", + ""$id"": ""http://api.example.com/profile.json"", + ""title"": ""The Root Schema"", + ""type"": ""object"", + ""required"": [ + ""minimum"", + ""workspace-v"" + ], + ""properties"": { + ""minimum"": { + ""type"": ""object"", + ""required"": [ + ""width_px"", + ""height_px"" + ], + ""properties"": { + ""width_px"": { + ""type"": ""integer"" + }, + ""height_px"": { + ""type"": ""integer"" + } + } + }, + ""workspace-v"": { + ""type"": ""object"", + ""required"": [ + ""x_px"", + ""y_px"", + ""width_px"", + ""height_px"", + ""width_mm"", + ""height_mm"" + ], + ""properties"": { + ""x_px"": { + ""type"": ""integer"" + }, + ""y_px"": { + ""type"": ""integer"" + }, + ""width_px"": { + ""type"": ""integer"" + }, + ""height_px"": { + ""type"": ""integer"" + }, + ""width_mm"": { + ""type"": ""integer"" + }, + ""height_mm"": { + ""type"": ""integer"" + } + } + }, + ""widgets"": { + ""type"": ""array"", + ""minProperties"": 1, + ""uniqueItems"": true, + ""items"": { + ""type"": ""string"", + ""pattern"": ""\\b(?:color|color_picker|center|resize|orientation)\\b$"" + } + } + }, + ""dependencies"": { + ""widgets"": { + ""orientation"": { + ""properties"": { + ""workspace-h"": { ""type"": ""string"" } + }, + ""required"": [""workspace-h""] + } + } + }, + ""dependentSchemas"": { + ""widgets"": { + ""properties"": { + ""workspace-h"": {""type"": ""string""} + } + } + } +}"; + + JSchema s = JSchema.Parse(json); + + JObject o = new JObject(); + Assert.IsFalse(o.IsValid(s)); + } } public sealed class JsonReaderStubWithIsClosed : JsonReader diff --git a/Src/Newtonsoft.Json.Schema/Infrastructure/Validation/ObjectScope.cs b/Src/Newtonsoft.Json.Schema/Infrastructure/Validation/ObjectScope.cs index 8eb3932d..072e87c1 100644 --- a/Src/Newtonsoft.Json.Schema/Infrastructure/Validation/ObjectScope.cs +++ b/Src/Newtonsoft.Json.Schema/Infrastructure/Validation/ObjectScope.cs @@ -442,8 +442,14 @@ public void InitializeScopes(JsonToken token) { if (dependency.Value is JSchema dependencySchema) { - SchemaScope scope = CreateTokenScope(token, dependencySchema, CreateConditionalContext(), null, InitialDepth); - _dependencyScopes!.Add(dependency.Key, scope); + ValidationUtils.Assert(_dependencyScopes != null); + + // Could be duplicated in "dependencies" and "dependentSchemas" + if (!_dependencyScopes.ContainsKey(dependency.Key)) + { + SchemaScope scope = CreateTokenScope(token, dependencySchema, CreateConditionalContext(), null, InitialDepth); + _dependencyScopes.Add(dependency.Key, scope); + } } } } @@ -453,8 +459,14 @@ public void InitializeScopes(JsonToken token) { if (dependency.Value is JSchema dependencySchema) { - SchemaScope scope = CreateTokenScope(token, dependencySchema, CreateConditionalContext(), null, InitialDepth); - _dependencyScopes!.Add(dependency.Key, scope); + ValidationUtils.Assert(_dependencyScopes != null); + + // Could be duplicated in "dependencies" and "dependentSchemas" + if (!_dependencyScopes.ContainsKey(dependency.Key)) + { + SchemaScope scope = CreateTokenScope(token, dependencySchema, CreateConditionalContext(), null, InitialDepth); + _dependencyScopes.Add(dependency.Key, scope); + } } } }