From ec6a38e052bf3d230e1a3c05a5ba15571916d00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl-Johan=20Sj=C3=B6gren?= Date: Tue, 20 May 2025 11:07:50 +0200 Subject: [PATCH] Fix for invalid state in JsonBlockValueConverter when an unused layout has a nested array (#19363) * Fix for invalid state in JsonBlockValueConverter when an unused layout has a nested array * Improved comments as suggested by copilot review, also fixed code style miss * Added check for malformed JSON with more closing array tokens then opening tokens --- .../Serialization/JsonBlockValueConverter.cs | 31 +++++++++++++++++-- .../JsonBlockValueConverterTests.cs | 29 +++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Serialization/JsonBlockValueConverter.cs b/src/Umbraco.Infrastructure/Serialization/JsonBlockValueConverter.cs index 6716dfae71..71624b6529 100644 --- a/src/Umbraco.Infrastructure/Serialization/JsonBlockValueConverter.cs +++ b/src/Umbraco.Infrastructure/Serialization/JsonBlockValueConverter.cs @@ -189,9 +189,36 @@ public class JsonBlockValueConverter : JsonConverter else { // ignore this layout - forward the reader to the end of the array and look for the next one - while (reader.TokenType is not JsonTokenType.EndArray) + + // Read past the current StartArray token before we start counting + _ = reader.Read(); + + // Keep track of the number of open arrays to ensure we find the correct EndArray token + var openCount = 0; + while (true) { - reader.Read(); + if (reader.TokenType is JsonTokenType.EndArray && openCount == 0) + { + break; + } + + if (reader.TokenType is JsonTokenType.StartArray) + { + openCount++; + } + else if (reader.TokenType is JsonTokenType.EndArray) + { + openCount--; + if (openCount < 0) + { + throw new JsonException($"Malformed JSON: Encountered more closing array tokens than opening ones while processing block editor alias: {blockEditorAlias}."); + } + } + + if (!reader.Read()) + { + throw new JsonException($"Unexpected end of JSON while looking for the end of the layout items array for block editor alias: {blockEditorAlias}."); + } } } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/JsonBlockValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/JsonBlockValueConverterTests.cs index 7377efdf35..7a4599540e 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/JsonBlockValueConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/JsonBlockValueConverterTests.cs @@ -441,4 +441,33 @@ public class JsonBlockValueConverterTests Assert.AreEqual(settingsElementKey1, layoutItems.First().SettingsKey); }); } + + [Test] + public void Try_Deserialize_Unknown_Block_Layout_With_Nested_Array() + { + var json = """ + { + "layout": { + "Umbraco.BlockGrid": [{ + "contentUdi": "umb://element/1304E1DDAC87439684FE8A399231CB3D", + "rowSpan": 1, + "areas": [], + "columnSpan": 12 + } + ], + "Umbraco.BlockList": [{ + "contentUdi": "umb://element/1304E1DDAC87439684FE8A399231CB3D" + } + ], + "Some.Custom.BlockEditor": [{ + "contentUdi": "umb://element/1304E1DDAC87439684FE8A399231CB3D" + } + ] + } + } +"""; + + var serializer = new SystemTextJsonSerializer(); + Assert.DoesNotThrow(() => serializer.Deserialize(json)); + } }