Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions policies/dotnet/Devolutions.Now.Policy.Model.Tests/PolicyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ public class PolicyTests
public static IEnumerable<object[]> PolicySamples() =>
Directory.GetFiles(SamplesDir, "*.policy.json").Select(f => new object[] { f });

public static TheoryData<string, int> ConstraintTextCollections() => new()
{
{ nameof(PolicyConstraints.AllowedInstallLocationPatterns), 256 },
{ nameof(PolicyConstraints.AllowedCustomParameters), 512 },
{ nameof(PolicyConstraints.AllowedCustomParameterPatterns), 512 },
{ nameof(PolicyConstraints.DeniedCustomParameters), 512 },
};

[Fact]
public void Tests_run_with_reflection_json_disabled()
{
Expand Down Expand Up @@ -322,6 +330,45 @@ public void Policy_text_lists_count_unicode_scalars_at_length_boundaries(string
Assert.Throws<JsonException>(() => PolicyDocument.ParseJson(document.ToJsonString()));
}

[Theory]
[MemberData(nameof(ConstraintTextCollections))]
public void Direct_policy_constraints_reject_invalid_bounded_strings(string collectionName, int maximum)
{
foreach (var value in new[] { "", new string('x', maximum + 1) })
{
var constraints = CreateConstraints(collectionName, value);
var json = new JsonObject { [collectionName] = new JsonArray(value) }.ToJsonString();

Assert.Throws<JsonException>(() => PolicySerializer.Serialize(constraints));
Assert.Throws<JsonException>(() => PolicySerializer.DeserializeStrict<PolicyConstraints>(json));

foreach (var options in new[] { PolicySerializer.Options, PolicySerializer.StrictOptions })
{
Assert.Throws<JsonException>(() => JsonSerializer.Serialize(constraints, options));
Assert.Throws<JsonException>(
() => JsonSerializer.Deserialize<PolicyConstraints>(json, options));
}
}
}

[Theory]
[MemberData(nameof(ConstraintTextCollections))]
public void Direct_policy_constraints_accept_valid_boundary_strings(string collectionName, int maximum)
{
var value = string.Concat(Enumerable.Repeat("\U0001F600", maximum));
var constraints = CreateConstraints(collectionName, value);
var json = new JsonObject { [collectionName] = new JsonArray(value) }.ToJsonString();

Assert.NotNull(PolicySerializer.Serialize(constraints));
Assert.NotNull(PolicySerializer.DeserializeStrict<PolicyConstraints>(json));

foreach (var options in new[] { PolicySerializer.Options, PolicySerializer.StrictOptions })
{
Assert.NotNull(JsonSerializer.Serialize(constraints, options));
Assert.NotNull(JsonSerializer.Deserialize<PolicyConstraints>(json, options));
}
}

[Fact]
public void Draft_rejects_server_managed_metadata()
{
Expand All @@ -339,6 +386,30 @@ private static PolicyDocument ParsePolicy(string path)

private static JsonNode ParseJsonString(string value) => JsonNode.Parse($"\"{value}\"")!;

private static PolicyConstraints CreateConstraints(string collectionName, string value)
{
var constraints = new PolicyConstraints();
switch (collectionName)
{
case nameof(PolicyConstraints.AllowedInstallLocationPatterns):
constraints.AllowedInstallLocationPatterns = [value];
break;
case nameof(PolicyConstraints.AllowedCustomParameters):
constraints.AllowedCustomParameters = [value];
break;
case nameof(PolicyConstraints.AllowedCustomParameterPatterns):
constraints.AllowedCustomParameterPatterns = [value];
break;
case nameof(PolicyConstraints.DeniedCustomParameters):
constraints.DeniedCustomParameters = [value];
break;
default:
throw new ArgumentOutOfRangeException(nameof(collectionName), collectionName, null);
}

return constraints;
}

private static string ResolvePolicyCrateRoot([CallerFilePath] string thisFile = "")
{
var testsDir = Path.GetDirectoryName(thisFile)!;
Expand Down
49 changes: 28 additions & 21 deletions policies/dotnet/Devolutions.Now.Policy.Model/PolicySerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ private static void ValidateSemanticValue(object? value)
case PolicyMatch match:
ValidateRequiredCollectionElements(match, "$");
break;
case PolicyConstraints constraints:
ValidateRequiredCollectionElements(constraints, "$");
break;
}
}

Expand Down Expand Up @@ -96,30 +99,34 @@ private static void ValidateRequiredCollectionElements(PolicyRule rule, string p

if (rule.Constraints is { } constraints)
{
var constraintsPath = $"{path}.Constraints";
RejectBoundedStrings(
constraints.AllowedInstallLocationPatterns,
1,
256,
$"{constraintsPath}.AllowedInstallLocationPatterns");
RejectBoundedStrings(
constraints.AllowedCustomParameters,
1,
512,
$"{constraintsPath}.AllowedCustomParameters");
RejectBoundedStrings(
constraints.AllowedCustomParameterPatterns,
1,
512,
$"{constraintsPath}.AllowedCustomParameterPatterns");
RejectBoundedStrings(
constraints.DeniedCustomParameters,
1,
512,
$"{constraintsPath}.DeniedCustomParameters");
ValidateRequiredCollectionElements(constraints, $"{path}.Constraints");
}
}

private static void ValidateRequiredCollectionElements(PolicyConstraints constraints, string path)
{
RejectBoundedStrings(
constraints.AllowedInstallLocationPatterns,
1,
256,
$"{path}.AllowedInstallLocationPatterns");
RejectBoundedStrings(
constraints.AllowedCustomParameters,
1,
512,
$"{path}.AllowedCustomParameters");
RejectBoundedStrings(
constraints.AllowedCustomParameterPatterns,
1,
512,
$"{path}.AllowedCustomParameterPatterns");
RejectBoundedStrings(
constraints.DeniedCustomParameters,
1,
512,
$"{path}.DeniedCustomParameters");
}

private static void ValidateRequiredCollectionElements(PolicyMatch match, string path)
{
RejectBoundedStrings(match.Sources, 1, 256, $"{path}.Sources");
Expand Down