GHSA-xw6w-9jjh-p9cr: Scriban Remote Code Execution Vulnerability
MODERATEGHSA-xw6w-9jjh-p9cr is a MODERATE (CVSS 6.5) remote code execution vulnerability (CWE-400: Uncontrolled Resource Consumption) affecting Scriban libraries in the NuGet ecosystem. The issue involves scriban has multiple denial-of-service vectors via unbounded resource consumption during expression evaluation. Remediation is available by upgrading affected packages to version 7.0.0.
GHSA-xw6w-9jjh-p9cr at a Glance
Security Impact
CVSS: 6.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H).
How to Fix GHSA-xw6w-9jjh-p9cr
To remediate this vulnerability, upgrade all affected Scriban dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade Scriban to 7.0.0
Upgrade Scriban.Signed to 7.0.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for Scriban
How to Check if You Are Affected
To determine whether your application is affected by GHSA-xw6w-9jjh-p9cr, inspect your project dependencies for Scriban:
inspect dependencies for Scriban
Analyze the terminal output to locate any version of Scriban in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 7.0.0), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-400: Uncontrolled Resource Consumption, where the product does not properly control the allocation and maintenance of a limited resource. In Scriban, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
GHSA-xw6w-9jjh-p9cr Affected Scriban Versions
2 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
Scriban
|
NuGet |
< 7.0.0
|
7.0.0
|
|
Scriban.Signed
|
NuGet |
< 7.0.0
|
7.0.0
|
References
Frequently Asked Questions
What is GHSA-xw6w-9jjh-p9cr?
Which Scriban versions are affected?
What version fixes GHSA-xw6w-9jjh-p9cr?
Is Scriban affected?
Is Scriban.Signed affected?
What is the CVSS score of GHSA-xw6w-9jjh-p9cr?
What CWE is associated with GHSA-xw6w-9jjh-p9cr?
How can I check NuGet dependencies for GHSA-xw6w-9jjh-p9cr?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-xw6w-9jjh-p9cr",
"modified" : "2026-07-06T13:10:09Z",
"published" : "2026-03-24T22:16:01Z",
"aliases" : [ ],
"summary" : "Scriban has Multiple Denial-of-Service Vectors via Unbounded Resource Consumption During Expression Evaluation",
"details" : "## Summary\n\nScriban's expression evaluation contains three distinct code paths that allow an attacker who can supply a template to cause denial of service through unbounded memory allocation or CPU exhaustion. The existing safety controls (`LimitToString`, `LoopLimit`) do not protect these paths, giving applications a false sense of safety when evaluating untrusted templates.\n\n## Details\n\n### Vector 1: Unbounded string multiplication\n\nIn `ScriptBinaryExpression.cs`, the `CalculateToString` method handles the `string * int` operator by looping without any upper bound:\n\n```csharp\n// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:319-334\nvar leftText = context.ObjectToString(left);\nvar builder = new StringBuilder();\nfor (int i = 0; i < value; i++)\n{\n builder.Append(leftText);\n}\nreturn builder.ToString();\n```\n\nThe `LimitToString` safety control (default 1MB) does **not** protect this code path. It only applies to `ObjectToString` output conversions in `TemplateContext.Helpers.cs` (lines 101-121), not to intermediate string values constructed inside `CalculateToString`. The `LoopLimit` also does not apply because this is a C# `for` loop, not a template-level loop — `StepLoop()` is never called here.\n\n### Vector 2: Unbounded BigInteger shift left\n\nThe `CalculateLongWithInt` and `CalculateBigIntegerNoFit` methods handle `ShiftLeft` without any bound on the shift amount:\n\n```csharp\n// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:710-711\ncase ScriptBinaryOperator.ShiftLeft:\n return (BigInteger)left << (int)right;\n```\n\n```csharp\n// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:783-784\ncase ScriptBinaryOperator.ShiftLeft:\n return left << (int)right;\n```\n\nIn contrast, the `Power` operator at lines 722 and 795 uses `BigInteger.ModPow(left, right, MaxBigInteger)` to cap results. The `MaxBigInteger` constant (`BigInteger.One << 1024 * 1024`, defined at line 690) already exists but is never applied to shift operations.\n\n### Vector 3: LoopLimit bypass via range enumeration in builtin functions\n\nThe range operators `..` and `..<` produce lazy `IEnumerable<object>` iterators:\n\n```csharp\n// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:401-417\nprivate static IEnumerable<object> RangeInclude(BigInteger left, BigInteger right)\n{\n if (left < right)\n {\n for (var i = left; i <= right; i++)\n {\n yield return FitToBestInteger(i);\n }\n }\n // ...\n}\n```\n\nWhen these ranges are consumed by builtin functions, `LoopLimit` is completely bypassed because `StepLoop()` is only called in `ScriptForStatement` and `ScriptWhileStatement` — it is never called in any function under `src/Scriban/Functions/`. For example:\n\n- `ArrayFunctions.Size` (line 609) calls `.Cast<object>().Count()`, fully enumerating the range\n- `ArrayFunctions.Join` (line 388) iterates with `foreach` and appends to a `StringBuilder` with no size limit\n\n## PoC\n\n### Vector 1 — String multiplication OOM:\n```csharp\nvar template = Template.Parse(\"{{ 'AAAA' * 500000000 }}\");\nvar context = new TemplateContext();\n// context.LimitToString is 1048576 by default — does NOT protect this path\ntemplate.Render(context); // OutOfMemoryException: attempts ~2GB allocation\n```\n\n### Vector 2 — BigInteger shift OOM:\n```csharp\nvar template = Template.Parse(\"{{ 1 << 100000000 }}\");\nvar context = new TemplateContext();\ntemplate.Render(context); // Allocates BigInteger with 100M bits (~12.5MB)\n// {{ 1 << 2000000000 }} attempts ~250MB\n```\n\n### Vector 3 — LoopLimit bypass via range + builtin:\n```csharp\nvar template = Template.Parse(\"{{ (0..1000000000) | array.size }}\");\nvar context = new TemplateContext();\n// context.LoopLimit is 1000 — does NOT protect builtin function iteration\ntemplate.Render(context); // CPU exhaustion: enumerates 1 billion items\n```\n\n```csharp\nvar template = Template.Parse(\"{{ (0..10000000) | array.join ',' }}\");\nvar context = new TemplateContext();\ntemplate.Render(context); // Memory exhaustion: builds ~80MB+ joined string\n```\n\n## Impact\n\nAn attacker who can supply a Scriban template (common in CMS platforms, email templating systems, reporting tools, and other applications embedding Scriban) can cause denial of service by crashing the host process via `OutOfMemoryException` or exhausting CPU resources. This is particularly impactful because:\n\n1. Applications relying on the default safety controls (`LoopLimit=1000`, `LimitToString=1MB`) believe they are protected against resource exhaustion from untrusted templates, but these controls have gaps.\n2. A single malicious template expression is sufficient — no complex template logic is required.\n3. The `OutOfMemoryException` in vectors 1 and 2 typically terminates the entire process, not just the template evaluation.\n\n## Recommended Fix\n\n### Vector 1 — String multiplication: Check `LimitToString` before the loop\n\n```csharp\n// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs, before line 330\nvar leftText = context.ObjectToString(left);\nif (context.LimitToString > 0 && (long)value * leftText.Length > context.LimitToString)\n{\n throw new ScriptRuntimeException(span,\n $\"String multiplication would exceed LimitToString ({context.LimitToString} characters)\");\n}\nvar builder = new StringBuilder();\nfor (int i = 0; i < value; i++)\n```\n\n### Vector 2 — BigInteger shift: Cap the shift amount\n\n```csharp\n// src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs, lines 710-711 and 783-784\ncase ScriptBinaryOperator.ShiftLeft:\n if (right > 1048576) // Same as MaxBigInteger bit count\n throw new ScriptRuntimeException(span,\n $\"Shift amount {right} exceeds maximum allowed (1048576)\");\n return (BigInteger)left << (int)right;\n```\n\n### Vector 3 — Range + builtins: Add iteration counting to range iterators\n\nPass `TemplateContext` to `RangeInclude`/`RangeExclude` and enforce a limit:\n\n```csharp\nprivate static IEnumerable<object> RangeInclude(TemplateContext context, BigInteger left, BigInteger right)\n{\n var maxRange = context.LoopLimit > 0 ? context.LoopLimit : int.MaxValue;\n int count = 0;\n if (left < right)\n {\n for (var i = left; i <= right; i++)\n {\n if (++count > maxRange)\n throw new ScriptRuntimeException(context.CurrentNode.Span,\n $\"Range enumeration exceeds LoopLimit ({maxRange})\");\n yield return FitToBestInteger(i);\n }\n }\n // ... same for descending branch\n}\n```\n\nAlternatively, validate range size eagerly at creation time: `if (BigInteger.Abs(right - left) > maxRange) throw ...`",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "NuGet",
"name" : "Scriban"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "7.0.0"
} ]
} ]
}, {
"package" : {
"ecosystem" : "NuGet",
"name" : "Scriban.Signed"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "7.0.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/scriban/scriban/security/advisories/GHSA-xw6w-9jjh-p9cr"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/scriban/scriban"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-400" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-03-24T22:16:01Z",
"nvd_published_at" : null
}
}