代码之家  ›  专栏  ›  技术社区  ›  Mike

python ast.literal评估和日期时间

  •  12
  • Mike  · 技术社区  · 14 年前

    我有一根绳子 "{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}" 我想把它转换成它所代表的对象。使用 ast.literal_eval() 给予

    ValueError: malformed string; 
    

    因为它不允许构建对象(即 datetime 打电话)有什么可以得到的吗 ast 正确处理,或确保 eval 防止代码注入?

    5 回复  |  直到 9 年前
        1
  •  14
  •   Community uzul    7 年前

    跟进 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))
    

    产量

    {'the_datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)} ... [PASSED]
    __import__('os'); os.unlink ... [REJECTED]
    import os; os.unlink ... [REJECTED]
    import(os) ... [REJECTED]
    
        2
  •  8
  •   martineau Nae    14 年前

    你可以提取 (2010, 11, 21, 0, 56, 58) 字符串中的字符 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
    

    这将搜索模式 "datetime(<comma separated list of integers>)" ,并将文本整数值的列表传递给 ast.literal评估() 对于转换为元组——它应该总是成功的,并且是代码注入抗性的。我相信它被称为“上下文敏感字符串评估”或CSSE。

        3
  •  3
  •   GNUton    10 年前

    在必须解析datetime obj时,不要使用ast,而不是编写大量代码。您可以运行eval()。btw请注意,如果字符串可以包含不可靠的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
    

    享受!D

        4
  •  1
  •   Ignacio Vazquez-Abrams    14 年前

    使用 language services 要将其编译为AST,请浏览AST,确保它只包含白名单节点集,然后执行它。

        5
  •  -2
  •   macm    11 年前

    使用repr(datetime.datetime.utcnow())保存到dict或文件中。看,

    >>> import datetime
    >>> oarg = datetime.datetime.utcnow()
    >>> oarg
    datetime.datetime(2013, 2, 6, 12, 39, 51, 709024)
    >>> butiwantthis = repr(oarg)
    >>> butiwantthis
    'datetime.datetime(2013, 2, 6, 12, 39, 51, 709024)'
    >>> eval(oarg)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: eval() arg 1 must be a string, bytes or code object
    >>> eval(butiwantthis)
    datetime.datetime(2013, 2, 6, 12, 39, 51, 709024)
    

    酷!()!

    注意进口

    >>> from datetime import datetime, date, time
    >>> oarg = datetime.datetime.utcnow()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
    >>> oarg = datetime.utcnow()
    >>> oarg
    datetime.datetime(2013, 2, 6, 12, 41, 51, 870458)
    >>> butiwantthis = repr(oarg)
    >>> butiwantthis
    'datetime.datetime(2013, 2, 6, 12, 41, 51, 870458)'
    >>> eval(butiwantthis)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 1, in <module>
    AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
    >>> # wrong import
    

    很完美!

    Python 3.3