git.net

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Sandboxing eval() (was: Calculator)


On 2020-01-19, Jon Ribbens via Python-list <python-list at python.org> wrote:
> On 2020-01-19, musbur at posteo.org <musbur at posteo.org> wrote:
>> Is it actually possible to build a "sandbox" around eval [...]
>>
>> [...]
>> 
>> It works, but is it safe?
>
> No, not even slightly. If you want to do this you need to write your
> own interpreter that runs your own domain-specific language.

And for that, one is often pointed to the ast module as a starting
point.  Here's an excerpt from an expression evaluator I wrote as part
of a two-pass symbolic assembler. [NB: it may have security issues
too, but they should be easier to find/fix than anything involving
eval()]


# evaluate an arithmetic expression in the context of the symbol table.
# only the operators in the table below are supported

import ast,operator
operators = \
    {
    # unary
    ast.Invert: operator.invert,
    ast.USub:   operator.neg,
    ast.UAdd:   operator.pos,
    # binary
    ast.Add:    operator.iadd,
    ast.Sub:    operator.isub,
    ast.Mult:   operator.imul,
    ast.Div:    operator.idiv,
    ast.BitXor: operator.ixor,
    ast.BitAnd: operator.iand,
    ast.BitOr:  operator.ior,
    ast.LShift: operator.lshift,
    ast.RShift: operator.rshift,
    ast.Mod:    operator.mod,
    ast.Pow:    operator.pow,
    }

def _eval_expr(node):
    global symbolTable, PC
    if isinstance(node, ast.Name):
        if node.id == "PC":
            return PC
        if node.id not in symbolTable:
            raise Exception("name '%s' undefined" % node.id)
        return symbolTable[node.id]
    elif isinstance(node, ast.Num):
        return node.n
    elif isinstance(node, ast.Str):
        if len(node.s) != 1:
            raise Exception("invalid string constant '%s' must be single character" % node.s)
        return ord(node.s[0])
    elif isinstance(node, ast.operator) or isinstance(node, ast.unaryop):
        if type(node) not in operators:
            errormsg(repr(dir(node)))
            raise Exception("illegal operator '%s" % node)
        return operators[type(node)]
    elif isinstance(node, ast.BinOp):
        return _eval_expr(node.op)(_eval_expr(node.left), _eval_expr(node.right))
    elif isinstance(node, ast.UnaryOp):
        return _eval_expr(node.op)(_eval_expr(node.operand))
    else:
        raise Exception("unsupported node type %s" %  node)

def eval_expr(expr):
    return _eval_expr(ast.parse(expr).body[0].value)