-
-
Notifications
You must be signed in to change notification settings - Fork 397
Description
Scriban's loop limit enforcement fails when using nested loops, allowing templates to exceed the configured LoopLimit without throwing the expected TemplateEvaluationException.
Expected Behavior
When a template contains loops that exceed the configured LoopLimit, Scriban should throw a TemplateEvaluationException with the message:
Exceeding number of iteration limit `{limit}` for loop statement.
Actual Behavior
Templates with nested loops that exceed the LoopLimit render successfully without throwing any exception, bypassing the loop limit enforcement entirely.
Minimal Reproduction
using System;
using System.Linq;
using Scriban;
class Program
{
static void Main()
{
// BUG: Nested loops bypass loop limit enforcement
var template = Template.Parse(@"
{{~
items = [" + string.Join(",\n", Enumerable.Range(1, 300).Select(i =>
$" {{ name: 'item{i}', tags: ['tag{i}'] }}")) + @"
]
~}}
{{~ for item in items ~}}
{{ item.name }} {{~ for tag in item.tags ~}}{{ tag }}{{~ end ~}}
{{~ end ~}}
");
var context = new TemplateContext { LoopLimit = 200 };
try
{
var result = template.Render(context);
Console.WriteLine($"BUG: Should have thrown TemplateEvaluationException");
Console.WriteLine($"Loop limit: {context.LoopLimit}");
Console.WriteLine($"Items processed: 300"); // Exceeds limit of 200
}
catch (Exception ex)
{
Console.WriteLine($"Working correctly: {ex.Message}");
}
}
}Test Results
- Single loop with 300 items: ✅ Correctly throws exception (loop limit works)
- Nested loops with 300 items: ❌ Bug - succeeds when it should fail (loop limit bypassed)
Root Cause Analysis
The issue appears to be in the TemplateContext.StepLoop method, which is responsible for tracking loop iterations and enforcing the LoopLimit. The method fails to properly count iterations when there are nested loop structures.
Problematic Pattern:
{{~ for item in items ~}} <!-- Outer loop: 300 iterations -->
{{~ for tag in item.tags ~}} <!-- Inner loop: 1 iteration per item -->
{{ tag }}
{{~ end ~}}
{{~ end ~}}
Impact
This bug allows templates to consume excessive resources as the loop limit safety mechanism is bypassed.
Workaround
Avoid using nested loops in templates where loop limit enforcement is critical, or implement additional validation in the application layer to detect and prevent excessive loop iterations.
I will try and figure out the fix required within Scriban.