Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FluidTemplate.TryParse and FluidTemplateExtensions.Render throw some unexpected exceptions #148

Closed
Metalnem opened this issue Aug 31, 2019 · 1 comment

Comments

@Metalnem
Copy link

FluidTemplate.TryParse and FluidTemplateExtensions.Render can throw bunch of unexpected exceptions. The ones I've found so far are:

  • ArgumentException
  • ArgumentOutOfRangeException
  • DivideByZeroException
  • NullReferenceException
  • OverflowException

To reproduce all of them, run the following program on .NET Core 2.2 with the latest Fluid beta (Fluid.Core 1.0.0-beta-9588):

using System;
using System.Collections.Generic;

namespace Fluid.Fuzz
{
	public class User
	{
		public string String { get; set; }
		public int Integer { get; set; }
		public List<double> Doubles { get; set; }
	}

	public class Program
	{
		public static void Main(string[] args)
		{
			var model = new User
			{
				String = "ABC",
				Integer = 123,
				Doubles = new List<double> { 1.1, 2.2, 3.3 }
			};

			var templates = new List<string>
			{
				"{{0|remove}}",
				"{{0|modulo}}",
				"{%endfor%}",
				"{%comment%}{%",
				"<p>{{false|divided_by|modulo|urlode}}<<"
			};

			foreach (var template in templates)
			{
				Run(model, template);
			}
		}

		private static void Run(User model, string template)
		{
			try
			{
				if (FluidTemplate.TryParse(template, out var result))
				{
					TemplateContext.GlobalMemberAccessStrategy.Register<User>();
					result.Render(new TemplateContext { Model = model });
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex);
			}
		}
	}
}

It should produce output similar to this:

System.ArgumentException: String cannot be of zero length.
Parameter name: oldValue
   at System.String.Replace(String oldValue, String newValue)
   at Fluid.Filters.StringFilters.Remove(FluidValue input, FilterArguments arguments, TemplateContext context)
   at Fluid.FilterCollection.<>c__DisplayClass1_0.<AddFilter>b__0(FluidValue input, FilterArguments arguments, TemplateContext context)
   at Fluid.Ast.FilterExpression.EvaluateAsync(TemplateContext context)
   at Fluid.Ast.OutputStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.BaseFluidTemplate`1.RenderAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TemplateContext context, TextEncoder encoder)
   at Fluid.FluidTemplateExtensions.Render(IFluidTemplate template, TemplateContext context)
   at Fluid.Fuzz.Program.Run(User model, String template) in /Users/Metalnem/Projects/sharpfuzz-samples/Fluid/Fluid.Fuzz/Program.cs:line 46
System.DivideByZeroException: Attempted to divide by zero.
   at Fluid.Filters.NumberFilters.Modulo(FluidValue input, FilterArguments arguments, TemplateContext context)
   at Fluid.FilterCollection.<>c__DisplayClass1_0.<AddFilter>b__0(FluidValue input, FilterArguments arguments, TemplateContext context)
   at Fluid.Ast.FilterExpression.EvaluateAsync(TemplateContext context)
   at Fluid.Ast.OutputStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.BaseFluidTemplate`1.RenderAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TemplateContext context, TextEncoder encoder)
   at Fluid.FluidTemplateExtensions.Render(IFluidTemplate template, TemplateContext context)
   at Fluid.Fuzz.Program.Run(User model, String template) in /Users/Metalnem/Projects/sharpfuzz-samples/Fluid/Fluid.Fuzz/Program.cs:line 46
System.NullReferenceException: Object reference not set to an instance of an object.
   at Fluid.DefaultFluidParser.BuildForStatement(BlockContext context)
   at Fluid.DefaultFluidParser.BuildTagStatement(ParseTreeNode node)
   at Fluid.DefaultFluidParser.TryParse(String template, Boolean stripEmptyLines, List`1& result, IEnumerable`1& errors)
   at Fluid.BaseFluidTemplate`1.TryParse(String template, Boolean stripEmptyLines, T& result, IEnumerable`1& errors)
   at Fluid.BaseFluidTemplate`1.TryParse(String template, T& result)
   at Fluid.Fuzz.Program.Run(User model, String template) in /Users/Metalnem/Projects/sharpfuzz-samples/Fluid/Fluid.Fuzz/Program.cs:line 43
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: offset
   at Microsoft.Extensions.Primitives.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument)
   at Microsoft.Extensions.Primitives.StringSegment.Subsegment(Int32 offset, Int32 length)
   at Fluid.DefaultFluidParser.ConsumeTag(StringSegment segment, Int32 start, String endTag, Int32& end)
   at Fluid.DefaultFluidParser.TryParse(String template, Boolean stripEmptyLines, List`1& result, IEnumerable`1& errors)
   at Fluid.BaseFluidTemplate`1.TryParse(String template, Boolean stripEmptyLines, T& result, IEnumerable`1& errors)
   at Fluid.BaseFluidTemplate`1.TryParse(String template, T& result)
   at Fluid.Fuzz.Program.Run(User model, String template) in /Users/Metalnem/Projects/sharpfuzz-samples/Fluid/Fluid.Fuzz/Program.cs:line 43
System.OverflowException: Value was either too large or too small for an Int32.
   at System.Convert.ToInt32(Double value)
   at Fluid.Filters.NumberFilters.Modulo(FluidValue input, FilterArguments arguments, TemplateContext context)
   at Fluid.FilterCollection.<>c__DisplayClass1_0.<AddFilter>b__0(FluidValue input, FilterArguments arguments, TemplateContext context)
   at Fluid.Ast.FilterExpression.EvaluateAsync(TemplateContext context)
   at Fluid.Ast.FilterExpression.EvaluateAsync(TemplateContext context)
   at Fluid.Ast.OutputStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.BaseFluidTemplate`1.RenderAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
   at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TemplateContext context, TextEncoder encoder)
   at Fluid.FluidTemplateExtensions.Render(IFluidTemplate template, TemplateContext context)
   at Fluid.Fuzz.Program.Run(User model, String template) in /Users/Metalnem/Projects/sharpfuzz-samples/Fluid/Fluid.Fuzz/Program.cs:line 46

You can find the fuzzing project I'm using here, and some basic Liquid dictionary file here.

The fuzzing project and the dictionary can be greatly improved (for example, by using more Fluid functions, making a more complicated model, or introducing more data types), but I'm leaving that to you if you are interested :)

jafin added a commit to jafin/fluid that referenced this issue Sep 7, 2019
…#148  .  Filter expression guards are in place, but will throw a runtime error,  ideally this should materialize as a template validation error.
@jafin
Copy link
Contributor

jafin commented Sep 7, 2019

@Metalnem was having a crack at trying to fix these, I've wrapped your scenario in a test case, I've solved 2/5 but the filter parameter errors are being thrown at RenderAsync time, not in the Build phase, which is not ideal. If anyone has guidance on how to get possibly the BuildFilterExpression to guard against mandatory filter params is appreciated and I can try to get a full PR through. (https://github.com/jafin/fluid/tree/fix/Issue148_Tests)

EDIT: I have a branch up that can cover the 5 examples above, and return ParserErrors before hitting RenderAsync. NOTE: I have not investigated further any additional failures your fuzz tests produce, limiting scope only to the above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants