Reputation: 150
I tried executing
import ast
ast.literal_eval('5+5')
Then I got ValueError: malformed node or string on line 1: <ast.BinOp object at 0x70e6bd2830>
.
So then I tried another:
import ast
ast.literal_eval(str(5+5))
And it evaluates it successfully:
10
But when I finally used it on an actual command $math 5+5
@client.command()
async def math(ctx, expression):
resp = ast.literal_eval(str(expression))
await ctx.send(resp)
I still get the same error: ValueError: malformed node or string on line 1: <ast.BinOp object at 0x7d05357700>
for some reason. I'm using Python 3.10.5.
How can I solve this? Any help would be appreciated.
Whole code:
import discord, ast
from discord.ext import commands
TOKEN = ""
client = commands.Bot(command_prefix="$")
prefix = client.command_prefix
def init():
client.run(TOKEN, reconnect=False)
@client.event
async def on_connect():
print('Connecting to server...')
@client.event
async def on_ready():
await client.change_presence(status=discord.Status.idle)
print('Logged in as ' + str(client.user))
print('Prefix: ' + str(prefix))
@client.command()
async def math(ctx, expression):
resp = ast.literal_eval(str(expression))
await ctx.send(resp)
init()
Upvotes: 3
Views: 4203
Reputation: 3934
You're no longer allowed to do addition in literal_eval
since python 3.7.
I found some "safe expressions" to work with from this answer. Here's a simple demo:
import ast
value = input()
co = ast.parse(value, mode='eval')
for node in ast.walk(co):
if not isinstance(node, (ast.Expression, ast.Constant, ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod, ast.Pow, ast.BinOp, ast.USub, ast.UAdd, ast.UnaryOp)):
raise RuntimeError(f'Sorry! You gave an illegal syntax node: {node.__class__.__name__}')
print(eval(value))
This successfully evaluates valid math in python, such as 5 + 7*8 = 61
or 5 ** 7 = 78125
, and of course 0.1 + 0.2 = 0.30000000000000004
.
Any bad operations that can potentially cause problems, such as Call
or Name
(meaning variable_names_like_this
and function()
) are not allowed. If you find any allowed operations that I missed, you can simply add them to the tuple inside the isinstance
to allow them. You can precisely customize which syntax operations are allowed or not.
As a fair warning to anyone who is tempted to use eval
without restricting the syntax you can use, this can execute regardless of your namespace fencing. It gets the object
class, from which it gets a module to get __builtins__
, after which getting __import__
and doing bad things with that. For more danger, replace 'echo hi'
here with 'rm -rf /'
:
[v for c,v in [c for c in ''.__class__.__bases__[0].__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__.items() if c == '__import__'][0]('os').system('echo hi')
Upvotes: 2
Reputation: 16240
The error you are getting appears to be because, according to the documentation:
The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans,
None
andEllipsis
.
So when you do:
ast.literal_eval('5+5')
The problem is +
which is a BinOp
(which corresponds to the <ast.BinOp object at 0x7d05357700>
part of the error message) and cannot be used.
However, when you do:
ast.literal_eval(str(5+5))
The order of evaluation is as follows:
ast.literal_eval(str(5+5)) -> ast.literal_eval(str(10)) -> ast.literal_eval('10')
Which no longer has +
involved.
Upvotes: 0