Every CVE Search/ Get API key

CVE-2026-74783

Scriban 6.6.0 through 7.2.0 Parser Recursion Denial of Service

8.7 HighFix available

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that fails to stop recursive descent parsing of deeply nested expressions. Attackers can supply templates with deeply nested parentheses, array initializers, object initializers, or unary operators to trigger an uncatchable StackOverflowException that immediately terminates the host process.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
High

Exploit likelihood

0.34%Up 0.1 points since Aug 17, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.7

Scriban 6.6.0 through 7.2.0 Parser Recursion Denial of Service

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that fails to stop recursive descent parsing of deeply nested expressions. Attackers can supply templates with deeply nested parentheses, array initializers, object initializers, or unary operators to trigger an uncatchable StackOverflowException that immediately terminates the host process.

Record
CVE-2026-74783
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-674
Updated
2026-11-02
EUVDEUVD-2026-59949 8.7

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that fails to stop recursive descent parsing of deeply nested expressions. Attackers can supply templates with deeply nested parentheses, array initializers, object initializers, or unary operators to trigger an uncatchable StackOverflowException that immediately terminates the host process.

Record
EUVD-2026-59949
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-6q7j-xr26-3h2c 5.5

Scriban: ExpressionDepthLimit guard is non-enforcing — parser-recursion DoS in 6.6.0–7.2.0 (incomplete fix for GHSA-wgh7-7m3c-fx25 / GHSA-p6q4-fgr8-vx4p)

Summary

The ExpressionDepthLimit parser guard in Scriban does not actually stop parsing — it only logs a non-fatal error and lets recursive descent continue. As a result, a template containing a deeply nested expression (parentheses, array initializers, object initializers, or unary operators) drives the recursive-descent parser into a native stack overflow. The resulting StackOverflowException is uncatchable in .NET and immediately terminates the host process.

Any application that parses an attacker-influenced template — or that passes attacker-controlled strings to object.eval / object.eval_template — can be crashed by a single small request (roughly an 8 KB payload). This is a denial-of-service. It affects both Scriban-native (Template.Parse) and Liquid (Template.ParseLiquid) syntax modes, which share the same expression parser.

This re-opens two advisories that were reported as fixed: GHSA-wgh7-7m3c-fx25 ("Uncontrolled recursion in parser → StackOverflow", reported fixed in 6.6.0) and GHSA-p6q4-fgr8-vx4p ("StackOverflow via nested array initializers bypasses ExpressionDepthLimit", reported fixed in 7.0.0). Both fixes are incomplete: the limit they rely on never halts recursion. All releases 6.6.0 through 7.2.0 (current) are affected.

Details

The depth guard is EnterExpression() in src/Scriban/Parsing/Parser.Expressions.cs:

// src/Scriban/Parsing/Parser.Expressions.cs:1209-1218
private void EnterExpression()
{
    _expressionDepth++;
    var limit = Options.ExpressionDepthLimit;
    if (limit > 0 && !_isExpressionDepthLimitReached && _expressionDepth > limit)
    {
        LogError(GetSpanForToken(Previous), $"The statement depth limit `{limit}` was reached when parsing this statement");
        _isExpressionDepthLimitReached = true;
    }
}

When the limit is exceeded it calls LogError(...) and sets a flag. It does not throw, does not return a sentinel, and does not unwind the parse. LogError here uses the default isFatal: false, so it merely appends a message and sets HasErrors — parsing proceeds:

// src/Scriban/Parsing/Parser.cs:476-488
private void Log(LogMessage logMessage, bool isFatal = false)
{
    Messages.Add(logMessage);
    if (logMessage.Type == ParserMessageType.Error)
    {
        HasErrors = true;
        if (isFatal) _hasFatalError = true;   // not set on the depth-limit path
    }
}

The flag _isExpressionDepthLimitReached is consulted only to avoid logging the same error more than once — no code path uses it to stop descending. Confirmed by full-repo search (grep -rn "_isExpressionDepthLimitReached" src/): it appears in exactly four places — the field declaration (Parser.cs:40), a reset to false (Parser.cs:106), and within EnterExpression the dedup test (Parser.Expressions.cs:1213) and its assignment to true (:1216). The only read is the dedup test on line 1213; nothing else reads it. ParseExpression calls EnterExpression() and then continues straight into the token switch with no flag check:

// src/Scriban/Parsing/Parser.Expressions.cs:113 + 181-182
EnterExpression();
try
{
    ...
    case TokenType.OpenParen:
        leftOperand = ParseParenthesis();   // recurses back into ParseExpression
// src/Scriban/Parsing/Parser.Expressions.cs:984-1001
private ScriptExpression ParseParenthesis()
{
    var expression = Open<ScriptNestedExpression>();
    ExpectAndParseTokenTo(expression.OpenParen, TokenType.OpenParen);
    expression.Expression = ExpectAndParseExpression(expression);  // -> ParseExpression -> ParseParenthesis -> ...
    ...
}

Both Template.Parse (Scriban-native) and Template.ParseLiquid (Liquid-compatibility) front-ends share this same expression parser, so both entry points are affected.

So for input nested N levels deep, the parser recurses N levels deep regardless of ExpressionDepthLimit. There is no…

Record
GHSA-6q7j-xr26-3h2c
CVSS 4.0
5.5 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P
Weakness
CWE-674
Updated
2026-11-02
GitHub AdvisoriesGHSA-jg4x-p32p-q6xr 8.7

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that...

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that fails to stop recursive descent parsing of deeply nested expressions. Attackers can supply templates with deeply nested parentheses, array initializers, object initializers, or unary operators to trigger an uncatchable StackOverflowException that immediately terminates the host process.

Record
GHSA-jg4x-p32p-q6xr
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-674
Updated
2026-11-02
NVD 8.7

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that fails to stop recursive descent parsing of deeply nested expressions. Attackers can supply templates with deeply nested parentheses, array initializers, object initializers, or unary operators to trigger an uncatchable StackOverflowException that immediately terminates the host process.

Record
CVE-2026-74783
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-674
Updated
2026-11-02
OSV 8.7

Scriban 6.6.0 through 7.2.0 Parser Recursion Denial of Service

Scriban versions 6.6.0 through 7.2.0 contain a non-enforcing ExpressionDepthLimit guard that fails to stop recursive descent parsing of deeply nested expressions. Attackers can supply templates with deeply nested parentheses, array initializers, object initializers, or unary operators to trigger an uncatchable StackOverflowException that immediately terminates the host process.

Record
CVE-2026-74783
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-674
Updated
2026-11-02
OSVGHSA-6q7j-xr26-3h2c 5.5

Scriban: ExpressionDepthLimit guard is non-enforcing — parser-recursion DoS in 6.6.0–7.2.0 (incomplete fix for GHSA-wgh7-7m3c-fx25 / GHSA-p6q4-fgr8-vx4p)

Summary

The ExpressionDepthLimit parser guard in Scriban does not actually stop parsing — it only logs a non-fatal error and lets recursive descent continue. As a result, a template containing a deeply nested expression (parentheses, array initializers, object initializers, or unary operators) drives the recursive-descent parser into a native stack overflow. The resulting StackOverflowException is uncatchable in .NET and immediately terminates the host process.

Any application that parses an attacker-influenced template — or that passes attacker-controlled strings to object.eval / object.eval_template — can be crashed by a single small request (roughly an 8 KB payload). This is a denial-of-service. It affects both Scriban-native (Template.Parse) and Liquid (Template.ParseLiquid) syntax modes, which share the same expression parser.

This re-opens two advisories that were reported as fixed: GHSA-wgh7-7m3c-fx25 ("Uncontrolled recursion in parser → StackOverflow", reported fixed in 6.6.0) and GHSA-p6q4-fgr8-vx4p ("StackOverflow via nested array initializers bypasses ExpressionDepthLimit", reported fixed in 7.0.0). Both fixes are incomplete: the limit they rely on never halts recursion. All releases 6.6.0 through 7.2.0 (current) are affected.

Details

The depth guard is EnterExpression() in src/Scriban/Parsing/Parser.Expressions.cs:

// src/Scriban/Parsing/Parser.Expressions.cs:1209-1218
private void EnterExpression()
{
    _expressionDepth++;
    var limit = Options.ExpressionDepthLimit;
    if (limit > 0 && !_isExpressionDepthLimitReached && _expressionDepth > limit)
    {
        LogError(GetSpanForToken(Previous), $"The statement depth limit `{limit}` was reached when parsing this statement");
        _isExpressionDepthLimitReached = true;
    }
}

When the limit is exceeded it calls LogError(...) and sets a flag. It does not throw, does not return a sentinel, and does not unwind the parse. LogError here uses the default isFatal: false, so it merely appends a message and sets HasErrors — parsing proceeds:

// src/Scriban/Parsing/Parser.cs:476-488
private void Log(LogMessage logMessage, bool isFatal = false)
{
    Messages.Add(logMessage);
    if (logMessage.Type == ParserMessageType.Error)
    {
        HasErrors = true;
        if (isFatal) _hasFatalError = true;   // not set on the depth-limit path
    }
}

The flag _isExpressionDepthLimitReached is consulted only to avoid logging the same error more than once — no code path uses it to stop descending. Confirmed by full-repo search (grep -rn "_isExpressionDepthLimitReached" src/): it appears in exactly four places — the field declaration (Parser.cs:40), a reset to false (Parser.cs:106), and within EnterExpression the dedup test (Parser.Expressions.cs:1213) and its assignment to true (:1216). The only read is the dedup test on line 1213; nothing else reads it. ParseExpression calls EnterExpression() and then continues straight into the token switch with no flag check:

// src/Scriban/Parsing/Parser.Expressions.cs:113 + 181-182
EnterExpression();
try
{
    ...
    case TokenType.OpenParen:
        leftOperand = ParseParenthesis();   // recurses back into ParseExpression
// src/Scriban/Parsing/Parser.Expressions.cs:984-1001
private ScriptExpression ParseParenthesis()
{
    var expression = Open<ScriptNestedExpression>();
    ExpectAndParseTokenTo(expression.OpenParen, TokenType.OpenParen);
    expression.Expression = ExpectAndParseExpression(expression);  // -> ParseExpression -> ParseParenthesis -> ...
    ...
}

Both Template.Parse (Scriban-native) and Template.ParseLiquid (Liquid-compatibility) front-ends share this same expression parser, so both entry points are affected.

So for input nested N levels deep, the parser recurses N levels deep regardless of ExpressionDepthLimit. There is no…

Record
GHSA-6q7j-xr26-3h2c
CVSS 4.0
5.5 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P
Weakness
CWE-674
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
Scriban
NuGet
6.6.0 to 7.2.0
7.2.1
Scriban.Signed
NuGet
6.6.0 to 7.2.0
7.2.1
Scriban
NuGet
6.6.0 up to 7.2.1, plus listed versions
7.0.07.0.17.0.27.0.37.0.4
Show 4 moreShow less
7.0.57.0.67.1.07.2.0
7.2.1
Scriban.Signed
NuGet
6.6.0 up to 7.2.1, plus listed versions
7.0.07.0.17.0.27.0.37.0.4
Show 4 moreShow less
7.0.57.0.67.1.07.2.0
7.2.1

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-74783 is a high vulnerability (CVSS 8.7) in Scriban, published June 26, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.34%, the 27th percentile. Fixed in Scriban 7.2.1.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open