Skip to content

Commit

Permalink
Prevent stack overflow in MiscFilters.WriteJson (#352)
Browse files Browse the repository at this point in the history
Fixes #351
  • Loading branch information
dafergu2 authored May 27, 2021
1 parent d798185 commit 7ef1948
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 2 deletions.
78 changes: 78 additions & 0 deletions Fluid.Tests/MiscFiltersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,33 @@ public async Task JsonShouldHideMembers()
Assert.Equal(expected, result.ToStringValue());
}

[Fact]
public async Task JsonShouldHandleCircularReferences()
{
var model = TestObjects.RecursiveReferenceObject;
var input = FluidValue.Create(model, TemplateOptions.Default);
var to = new TemplateOptions();
to.MemberAccessStrategy.Register<TestObjects.Node>();

var result = await MiscFilters.Json(input, new FilterArguments(), new TemplateContext(to));

Assert.Equal("{\"Name\":\"Object1\",\"NodeRef\":{\"Name\":\"Child1\",\"NodeRef\":\"circular reference detected.\"}}", result.ToStringValue());
}

[Fact]
public async Task JsonShouldHandleCircularReferencesOnSiblingPropertiesSeparately()
{
var model = TestObjects.SiblingPropertiesHaveSameReferenceObject;
var input = FluidValue.Create(model, TemplateOptions.Default);
var to = new TemplateOptions();
to.MemberAccessStrategy.Register<TestObjects.Node>();
to.MemberAccessStrategy.Register<TestObjects.MultipleNode>();

var result = await MiscFilters.Json(input, new FilterArguments(), new TemplateContext(to));

Assert.Equal("{\"Name\":\"MultipleNode1\",\"Node1\":{\"Name\":\"Object1\",\"NodeRef\":{\"Name\":\"Child1\",\"NodeRef\":\"circular reference detected.\"}},\"Node2\":{\"Name\":\"Object1\",\"NodeRef\":{\"Name\":\"Child1\",\"NodeRef\":\"circular reference detected.\"}}}", result.ToStringValue());
}

[Theory]
[InlineData("", "", "", "0")]
[InlineData(123456, "", "", "123456")]
Expand Down Expand Up @@ -565,6 +592,57 @@ public async Task FormatString(object input, object[] args, string culture, stri
Assert.Equal(expected, result.ToStringValue());
}

public static class TestObjects
{
public class Node
{
public string Name { get; set; }
public Node NodeRef { get; set; }
}

public class MultipleNode
{
public string Name { get; set; }

public Node Node1 { get; set; }

public Node Node2 { get; set; }
}

public static Node RecursiveReferenceObject
{
get
{
var parent = new Node
{
Name = "Object1",
};
var child = new Node
{
Name = "Child1",
NodeRef = parent
};
parent.NodeRef = child;
return parent;
}
}

public static object SiblingPropertiesHaveSameReferenceObject
{
get
{
var n = RecursiveReferenceObject;
var m = new MultipleNode
{
Name = "MultipleNode1",
Node1 = n,
Node2 = n
};
return m;
}
}
}

private class JsonAccessStrategy
{
public string Visible { get; set; } = "Visible";
Expand Down
12 changes: 10 additions & 2 deletions Fluid/Filters/MiscFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ private static bool TryGetDateTimeInput(FluidValue input, TemplateContext contex
return true;
}

private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input, TemplateContext ctx)
private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input, TemplateContext ctx, HashSet<object> stack = null)
{
switch (input.Type)
{
Expand Down Expand Up @@ -614,14 +614,22 @@ private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input
value = access.Get(obj, name, ctx);
}

stack ??= new HashSet<object>();
if (stack.Contains(value))
{
value = "circular reference detected.";
}

var fluidValue = FluidValue.Create(value, ctx.Options);
if (fluidValue.IsNil())
{
continue;
}

writer.WritePropertyName(name);
await WriteJson(writer, fluidValue, ctx);
stack.Add(obj);
await WriteJson(writer, fluidValue, ctx, stack);
stack.Remove(obj);
}

writer.WriteEndObject();
Expand Down

0 comments on commit 7ef1948

Please sign in to comment.