Add MAX_STRING_SIZE limit to sandbox string multiplication
SandboxedEnvironment limits range() via MAX_RANGE to prevent DoS from
large sequences, but does not limit string multiplication. A template
expression like {{ "A" * 10**9 }} allocates 1GB of memory instantly.
Add MAX_STRING_SIZE (default 1,000,000) and a safe_mul function that
checks result size before performing string repetition. Wire it into
the default binop_table and intercepted_binops so the sandbox
intercepts * operations by default.
Normal arithmetic multiplication, small string repetition, and list
multiplication are all unaffected. Only str * int exceeding the limit
is blocked.
This commit is contained in:
parent
5ef70112a1
commit
8bb276db8b
@ -24,6 +24,9 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
||||
#: maximum number of items a range may produce
|
||||
MAX_RANGE = 100000
|
||||
|
||||
#: maximum size of a string that can be produced by the ``*`` operator
|
||||
MAX_STRING_SIZE = 1000000
|
||||
|
||||
#: Unsafe function attributes.
|
||||
UNSAFE_FUNCTION_ATTRIBUTES: set[str] = set()
|
||||
|
||||
@ -99,6 +102,25 @@ def safe_range(*args: int) -> range:
|
||||
return rng
|
||||
|
||||
|
||||
def safe_mul(left: t.Any, right: t.Any) -> t.Any:
|
||||
"""A multiplication that prevents creating strings larger than
|
||||
MAX_STRING_SIZE via the ``*`` operator.
|
||||
"""
|
||||
if isinstance(left, str) and isinstance(right, int):
|
||||
if right > 0 and len(left) * right > MAX_STRING_SIZE:
|
||||
raise OverflowError(
|
||||
"String repetition too large. The sandbox blocks strings"
|
||||
f" larger than MAX_STRING_SIZE ({MAX_STRING_SIZE})."
|
||||
)
|
||||
elif isinstance(right, str) and isinstance(left, int):
|
||||
if left > 0 and len(right) * left > MAX_STRING_SIZE:
|
||||
raise OverflowError(
|
||||
"String repetition too large. The sandbox blocks strings"
|
||||
f" larger than MAX_STRING_SIZE ({MAX_STRING_SIZE})."
|
||||
)
|
||||
return operator.mul(left, right)
|
||||
|
||||
|
||||
def unsafe(f: F) -> F:
|
||||
"""Marks a function or method as unsafe.
|
||||
|
||||
@ -193,7 +215,7 @@ class SandboxedEnvironment(Environment):
|
||||
default_binop_table: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
"+": operator.add,
|
||||
"-": operator.sub,
|
||||
"*": operator.mul,
|
||||
"*": safe_mul,
|
||||
"/": operator.truediv,
|
||||
"//": operator.floordiv,
|
||||
"**": operator.pow,
|
||||
@ -222,7 +244,7 @@ class SandboxedEnvironment(Environment):
|
||||
#: interested in.
|
||||
#:
|
||||
#: .. versionadded:: 2.6
|
||||
intercepted_binops: frozenset[str] = frozenset()
|
||||
intercepted_binops: frozenset[str] = frozenset(["*"])
|
||||
|
||||
#: a set of unary operators that should be intercepted. Each operator
|
||||
#: that is added to this set (empty by default) is delegated to the
|
||||
|
||||
Loading…
Reference in New Issue
Block a user