GHSA-xw6w-9jjh-p9cr: Scriban Remote Code Execution Vulnerability

MODERATE
CVSS 6.5
CWE-400
Published
Advisory Summary

GHSA-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

CVE Identifier N/A
GHSA Identifier GHSA-xw6w-9jjh-p9cr
Severity MODERATE
CVSS v3.1 Base Score 6.5
CWE Classification CWE-400: Uncontrolled Resource Consumption
Affected Product Scriban
Package Ecosystem NuGet
Published Date
Last Updated

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).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity None (I:N)
Availability High (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.

Scriban fixed: 7.0.0
Upgrade Scriban to 7.0.0
Scriban.Signed fixed: 7.0.0
Upgrade Scriban.Signed to 7.0.0
Verify Direct and Transitive Dependencies

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

## Summary Scriban'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. ## Details ### Vector 1: Unbounded string multiplication In `ScriptBinaryExpression.cs`, the `CalculateToString` method handles the `string * int` operator by looping without any upper bound: ```csharp // src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:319-334 var leftText = context.ObjectToString(left); var builder = new StringBuilder(); for (int i = 0; i < value; i++) { builder.Append(leftText); } return builder.ToString(); ``` The `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. ### Vector 2: Unbounded BigInteger shift left The `CalculateLongWithInt` and `CalculateBigIntegerNoFit` methods handle `ShiftLeft` without any bound on the shift amount: ```csharp // src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:710-711 case ScriptBinaryOperator.ShiftLeft: return (BigInteger)left << (int)right; ``` ```csharp // src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:783-784 case ScriptBinaryOperator.ShiftLeft: return left << (int)right; ``` In 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. ### Vector 3: LoopLimit bypass via range enumeration in builtin functions The range operators `..` and `..<` produce lazy `IEnumerable<object>` iterators: ```csharp // src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs:401-417 private static IEnumerable<object> RangeInclude(BigInteger left, BigInteger right) { if (left < right) { for (var i = left; i <= right; i++) { yield return FitToBestInteger(i); } } // ... } ``` When 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: - `ArrayFunctions.Size` (line 609) calls `.Cast<object>().Count()`, fully enumerating the range - `ArrayFunctions.Join` (line 388) iterates with `foreach` and appends to a `StringBuilder` with no size limit ## PoC ### Vector 1 — String multiplication OOM: ```csharp var template = Template.Parse("{{ 'AAAA' * 500000000 }}"); var context = new TemplateContext(); // context.LimitToString is 1048576 by default — does NOT protect this path template.Render(context); // OutOfMemoryException: attempts ~2GB allocation ``` ### Vector 2 — BigInteger shift OOM: ```csharp var template = Template.Parse("{{ 1 << 100000000 }}"); var context = new TemplateContext(); template.Render(context); // Allocates BigInteger with 100M bits (~12.5MB) // {{ 1 << 2000000000 }} attempts ~250MB ``` ### Vector 3 — LoopLimit bypass via range + builtin: ```csharp var template = Template.Parse("{{ (0..1000000000) | array.size }}"); var context = new TemplateContext(); // context.LoopLimit is 1000 — does NOT protect builtin function iteration template.Render(context); // CPU exhaustion: enumerates 1 billion items ``` ```csharp var template = Template.Parse("{{ (0..10000000) | array.join ',' }}"); var context = new TemplateContext(); template.Render(context); // Memory exhaustion: builds ~80MB+ joined string ``` ## Impact An 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: 1. 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. 2. A single malicious template expression is sufficient — no complex template logic is required. 3. The `OutOfMemoryException` in vectors 1 and 2 typically terminates the entire process, not just the template evaluation. ## Recommended Fix ### Vector 1 — String multiplication: Check `LimitToString` before the loop ```csharp // src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs, before line 330 var leftText = context.ObjectToString(left); if (context.LimitToString > 0 && (long)value * leftText.Length > context.LimitToString) { throw new ScriptRuntimeException(span, $"String multiplication would exceed LimitToString ({context.LimitToString} characters)"); } var builder = new StringBuilder(); for (int i = 0; i < value; i++) ``` ### Vector 2 — BigInteger shift: Cap the shift amount ```csharp // src/Scriban/Syntax/Expressions/ScriptBinaryExpression.cs, lines 710-711 and 783-784 case ScriptBinaryOperator.ShiftLeft: if (right > 1048576) // Same as MaxBigInteger bit count throw new ScriptRuntimeException(span, $"Shift amount {right} exceeds maximum allowed (1048576)"); return (BigInteger)left << (int)right; ``` ### Vector 3 — Range + builtins: Add iteration counting to range iterators Pass `TemplateContext` to `RangeInclude`/`RangeExclude` and enforce a limit: ```csharp private static IEnumerable<object> RangeInclude(TemplateContext context, BigInteger left, BigInteger right) { var maxRange = context.LoopLimit > 0 ? context.LoopLimit : int.MaxValue; int count = 0; if (left < right) { for (var i = left; i <= right; i++) { if (++count > maxRange) throw new ScriptRuntimeException(context.CurrentNode.Span, $"Range enumeration exceeds LoopLimit ({maxRange})"); yield return FitToBestInteger(i); } } // ... same for descending branch } ``` Alternatively, validate range size eagerly at creation time: `if (BigInteger.Abs(right - left) > maxRange) throw ...`

GHSA-xw6w-9jjh-p9cr Affected Scriban Versions

2 component(s)
Fixed Versions:
7.0.0
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?
GHSA-xw6w-9jjh-p9cr is a security vulnerability in Scriban involving Scriban has Multiple Denial-of-Service Vectors via Unbounded Resource Consumption During Expression Evaluation under CWE-400: Uncontrolled Resource Consumption.
Which Scriban versions are affected?
Affected versions include releases prior to 7.0.0 across affected packages: Scriban, Scriban.Signed.
What version fixes GHSA-xw6w-9jjh-p9cr?
Upgrading to 7.0.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is Scriban affected?
Yes, Scriban is affected in releases prior to 7.0.0. Upgrade your dependency to 7.0.0 or later.
Is Scriban.Signed affected?
Yes, Scriban.Signed is affected in releases prior to 7.0.0. Upgrade your dependency to 7.0.0 or later.
What is the CVSS score of GHSA-xw6w-9jjh-p9cr?
GHSA-xw6w-9jjh-p9cr has a CVSS v3.1 base score of 6.5 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H.
What CWE is associated with GHSA-xw6w-9jjh-p9cr?
GHSA-xw6w-9jjh-p9cr is classified under CWE-400: Uncontrolled Resource Consumption (The product does not properly control the allocation and maintenance of a limited resource.).
How can I check NuGet dependencies for GHSA-xw6w-9jjh-p9cr?
Run the dependency tree command: 'inspect dependencies for Scriban'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "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
  }
}