python ast.literal_eval和datetime

Mik*_*ike 13 python

我有一个字符串"{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}",我想将其转换为它所代表的对象.使用ast.literal_eval()给出

ValueError: malformed string; 
Run Code Online (Sandbox Code Playgroud)

因为它不允许构造对象(即datetime调用).无论如何要么ast正确处理这个,要么保护eval以防止代码注入?

unu*_*tbu 15

跟进Ignacio Vazquez-Abrams的想法:

import ast
import datetime

def parse_datetime_dict(astr,debug=False):
    try: tree=ast.parse(astr)
    except SyntaxError: raise ValueError(astr)
    for node in ast.walk(tree):
        if isinstance(node,(ast.Module,ast.Expr,ast.Dict,ast.Str,
                            ast.Attribute,ast.Num,ast.Name,ast.Load, ast.Tuple)): continue
        if (isinstance(node,ast.Call)
                and isinstance(node.func, ast.Attribute)
                and node.func.attr == 'datetime'): continue
        if debug:
            attrs=[attr for attr in dir(node) if not attr.startswith('__')]
            print(node)
            for attrname in attrs:
                print('    {k} ==> {v}'.format(k=attrname,v=getattr(node,attrname)))
        raise ValueError(astr)
    return eval(astr)

good_strings=["{'the_datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"]
bad_strings=["__import__('os'); os.unlink",
             "import os; os.unlink",
             "import(os)", # SyntaxError
             ]

for astr in good_strings:
    result=parse_datetime_dict(astr)    
    print('{s} ... [PASSED]'.format(s=astr))

for astr in bad_strings:
    try:
        result=parse_datetime_dict(astr)
    except ValueError:
        print('{s} ... [REJECTED]'.format(s=astr))
    else:
        sys.exit('ERROR: failed to catch {s!r}'.format(s=astr))
Run Code Online (Sandbox Code Playgroud)

产量

{'the_datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)} ... [PASSED]
__import__('os'); os.unlink ... [REJECTED]
import os; os.unlink ... [REJECTED]
import(os) ... [REJECTED]
Run Code Online (Sandbox Code Playgroud)


mar*_*eau 8

您可以(2010, 11, 21, 0, 56, 58)使用a从字符串中提取字符regex,将其传递ast.literal_eval()给获取元组,然后传递该元组datetime.datetime(*a_tuple)以获取对象.听起来很多,但每个步骤都非常简单(而且安全).这就是我在说的:

import ast
import datetime
import re

s = "{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"
m = re.search(r"""datetime(\((\d+)(,\s*\d+)*\))""", s)
if m:  # any matches?
    args = ast.literal_eval(m.group(1))
    print datetime.datetime(*args)
    # 2010-11-21 00:56:58
Run Code Online (Sandbox Code Playgroud)

这将"datetime(<comma separated list of integers>)"在字符串中搜索模式,并仅传递文字整数值列表以ast.literal_eval()转换为元组 - 这应该始终成功并且是代码注入抵抗的.我相信它被称为"上下文敏感字符串评估"或CSSE.


GNU*_*ton 5

不要编写大量代码,而是在必须解析日期时间对象时不要使用 ast。您可以运行 eval()。顺便说一句,请注意,如果字符串可以包含狡猾的 python 命令,则使用此函数可能会遇到安全问题。

下面是它的工作原理:

>>> x="{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"
>>> b=eval(x)
>>> b
{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}
>>> b["datetime"].year
2010
Run Code Online (Sandbox Code Playgroud)

享受!:D