简体   繁体   中英

malformed node or string on line 1 when I use ast.literal_eval using discord.py

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()

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')

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 and Ellipsis .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM