3454a55636
cffconvert / validate (push) Has been cancelled
ci-workflow / pre-commit (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (macos-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (ubuntu-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (windows-latest) (push) Has been cancelled
ci-workflow / Python 3.10 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.11 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.12 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.13 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14t on macos-latest (push) Has been cancelled
ci-workflow / Python 3.10 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.11 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.12 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.13 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14t on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.10 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.11 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.12 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.13 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.14 on windows-latest (push) Has been cancelled
48 lines
1.9 KiB
Python
48 lines
1.9 KiB
Python
"""Regression tests for unbounded recursion in the logic parser (CWE-674).
|
|
|
|
`LogicParser` is a recursive-descent parser: each nested sub-expression recurses
|
|
through `process_next_expression`. Deeply nested input would otherwise recurse
|
|
until Python raised an uncaught `RecursionError`, crashing the caller. The parser
|
|
now caps the nesting depth at `LogicParser.MAX_PARSE_DEPTH` and raises a normal
|
|
`LogicalExpressionException` instead.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from nltk.sem.logic import Expression, LogicalExpressionException, LogicParser
|
|
|
|
|
|
def test_deeply_nested_expression_raises_clean_error():
|
|
"""A pathologically deep expression raises a clean parse error, not an
|
|
uncaught RecursionError (uncontrolled recursion, CWE-674)."""
|
|
# If an uncaught RecursionError were raised instead, pytest.raises would not
|
|
# catch it and the test would fail with the unexpected exception.
|
|
with pytest.raises(LogicalExpressionException):
|
|
Expression.fromstring("-" * 5000 + "p") # 5000 nested negations
|
|
|
|
|
|
def test_normal_expressions_still_parse():
|
|
"""Ordinary expressions are unaffected by the depth cap."""
|
|
for s in [
|
|
"exists x.(P(x) & Q(x))",
|
|
"all x.(man(x) -> mortal(x))",
|
|
"\\x.(P(x))(a)",
|
|
"-(p & q)",
|
|
"P(a,b,c)",
|
|
]:
|
|
assert str(Expression.fromstring(s)) # parses without error
|
|
|
|
|
|
def test_depth_cap_is_configurable_and_enforced():
|
|
"""Nesting up to the cap parses; nesting beyond it raises a clean error."""
|
|
saved = LogicParser.MAX_PARSE_DEPTH
|
|
LogicParser.MAX_PARSE_DEPTH = 20
|
|
try:
|
|
# 19 nested negations: under the cap, parses fine.
|
|
assert str(Expression.fromstring("-" * 19 + "p"))
|
|
# 21 nested negations: over the cap, clean LogicalExpressionException.
|
|
with pytest.raises(LogicalExpressionException):
|
|
Expression.fromstring("-" * 21 + "p")
|
|
finally:
|
|
LogicParser.MAX_PARSE_DEPTH = saved
|