diff --git a/src/Core/AdminConsole/Utilities/PolicyDataValidator.cs b/src/Core/AdminConsole/Utilities/PolicyDataValidator.cs index 84e63f2a20..d533ca88cf 100644 --- a/src/Core/AdminConsole/Utilities/PolicyDataValidator.cs +++ b/src/Core/AdminConsole/Utilities/PolicyDataValidator.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models; @@ -30,7 +31,8 @@ public static class PolicyDataValidator switch (policyType) { case PolicyType.MasterPassword: - CoreHelpers.LoadClassFromJsonData(json); + var masterPasswordData = CoreHelpers.LoadClassFromJsonData(json); + ValidateModel(masterPasswordData, policyType); break; case PolicyType.SendOptions: CoreHelpers.LoadClassFromJsonData(json); @@ -44,11 +46,24 @@ public static class PolicyDataValidator } catch (JsonException ex) { - var fieldInfo = !string.IsNullOrEmpty(ex.Path) ? $": field '{ex.Path}' has invalid type" : ""; + var fieldName = !string.IsNullOrEmpty(ex.Path) ? ex.Path.TrimStart('$', '.') : null; + var fieldInfo = !string.IsNullOrEmpty(fieldName) ? $": {fieldName} has an invalid value" : ""; throw new BadRequestException($"Invalid data for {policyType} policy{fieldInfo}."); } } + private static void ValidateModel(object model, PolicyType policyType) + { + var validationContext = new ValidationContext(model); + var validationResults = new List(); + + if (!Validator.TryValidateObject(model, validationContext, validationResults, true)) + { + var errors = string.Join(", ", validationResults.Select(r => r.ErrorMessage)); + throw new BadRequestException($"Invalid data for {policyType} policy: {errors}"); + } + } + /// /// Validates and deserializes policy metadata based on the policy type. /// diff --git a/test/Core.Test/AdminConsole/Utilities/PolicyDataValidatorTests.cs b/test/Core.Test/AdminConsole/Utilities/PolicyDataValidatorTests.cs index 43725d23e0..2c94634f7f 100644 --- a/test/Core.Test/AdminConsole/Utilities/PolicyDataValidatorTests.cs +++ b/test/Core.Test/AdminConsole/Utilities/PolicyDataValidatorTests.cs @@ -19,12 +19,17 @@ public class PolicyDataValidatorTests [Fact] public void ValidateAndSerialize_ValidData_ReturnsSerializedJson() { - var data = new Dictionary { { "minLength", 12 } }; + var data = new Dictionary + { + { "minLength", 12 }, + { "minComplexity", 4 } + }; var result = PolicyDataValidator.ValidateAndSerialize(data, PolicyType.MasterPassword); Assert.NotNull(result); Assert.Contains("\"minLength\":12", result); + Assert.Contains("\"minComplexity\":4", result); } [Fact] @@ -56,4 +61,26 @@ public class PolicyDataValidatorTests Assert.IsType(result); } + + [Fact] + public void ValidateAndSerialize_ExcessiveMinLength_ThrowsBadRequestException() + { + var data = new Dictionary { { "minLength", 129 } }; + + var exception = Assert.Throws(() => + PolicyDataValidator.ValidateAndSerialize(data, PolicyType.MasterPassword)); + + Assert.Contains("Invalid data for MasterPassword policy", exception.Message); + } + + [Fact] + public void ValidateAndSerialize_ExcessiveMinComplexity_ThrowsBadRequestException() + { + var data = new Dictionary { { "minComplexity", 5 } }; + + var exception = Assert.Throws(() => + PolicyDataValidator.ValidateAndSerialize(data, PolicyType.MasterPassword)); + + Assert.Contains("Invalid data for MasterPassword policy", exception.Message); + } }