• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 8912ca5a8c3a9725c3ba6d30561607150a6faebe-PR-2205

pending completion
8912ca5a8c3a9725c3ba6d30561607150a6faebe-PR-2205

Pull #2205

github-actions

web-flow
Merge 81f463e2c into 8b4cacf8b
Pull Request #2205: Fix stable Python 2 installation from a built wheel

3878 of 6371 branches covered (60.87%)

12199 of 16604 relevant lines covered (73.47%)

0.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.15
/pwnlib/util/safeeval.py
1
from __future__ import division
1✔
2

3
_const_codes = [
1✔
4
    'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
5
    'BUILD_LIST','BUILD_MAP','BUILD_TUPLE','BUILD_SET',
6
    'BUILD_CONST_KEY_MAP', 'BUILD_STRING',
7
    'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR', 'STORE_MAP',
8
    'LIST_TO_TUPLE', 'LIST_EXTEND', 'SET_UPDATE', 'DICT_UPDATE', 'DICT_MERGE',
9
    'COPY', 'RESUME',
10
    ]
11

12
_expr_codes = _const_codes + [
1✔
13
    'UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT',
14
    'UNARY_INVERT','BINARY_POWER','BINARY_MULTIPLY',
15
    'BINARY_DIVIDE','BINARY_FLOOR_DIVIDE','BINARY_TRUE_DIVIDE',
16
    'BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT',
17
    'BINARY_LSHIFT','BINARY_RSHIFT','BINARY_AND','BINARY_XOR',
18
    'BINARY_OR',
19
    'BINARY_OP',
20
    ]
21

22
_values_codes = _expr_codes + ['LOAD_NAME']
1✔
23

24
import six
1✔
25

26
def _get_opcodes(codeobj):
1✔
27
    """_get_opcodes(codeobj) -> [opcodes]
28

29
    Extract the actual opcodes as a list from a code object
30

31
    >>> c = compile("[1 + 2, (1,2)]", "", "eval")
32
    >>> _get_opcodes(c)
33
    [100, 100, 103, 83]
34
    """
35
    import dis
1✔
36
    if hasattr(dis, 'get_instructions'):
1!
37
        return [ins.opcode for ins in dis.get_instructions(codeobj)]
×
38
    i = 0
1✔
39
    opcodes = []
1✔
40
    s = codeobj.co_code
1✔
41
    while i < len(s):
1✔
42
        code = six.indexbytes(s, i)
1✔
43
        opcodes.append(code)
1✔
44
        if code >= dis.HAVE_ARGUMENT:
1✔
45
            i += 3
1✔
46
        else:
47
            i += 1
1✔
48
    return opcodes
1✔
49

50
def test_expr(expr, allowed_codes):
1✔
51
    """test_expr(expr, allowed_codes) -> codeobj
52

53
    Test that the expression contains only the listed opcodes.
54
    If the expression is valid and contains only allowed codes,
55
    return the compiled code object. Otherwise raise a ValueError
56
    """
57
    import dis
1✔
58
    allowed_codes = [dis.opmap[c] for c in allowed_codes if c in dis.opmap]
1✔
59
    try:
1✔
60
        c = compile(expr, "", "eval")
1✔
61
    except SyntaxError:
1✔
62
        raise ValueError("%r is not a valid expression" % expr)
1✔
63
    codes = _get_opcodes(c)
1✔
64
    for code in codes:
1✔
65
        if code not in allowed_codes:
1✔
66
            raise ValueError("opcode %s not allowed" % dis.opname[code])
1✔
67
    return c
1✔
68

69
def const(expr):
1✔
70
    """const(expression) -> value
71

72
    Safe Python constant evaluation
73

74
    Evaluates a string that contains an expression describing
75
    a Python constant. Strings that are not valid Python expressions
76
    or that contain other code besides the constant raise ValueError.
77

78
    Examples:
79

80
        >>> const("10")
81
        10
82
        >>> const("[1,2, (3,4), {'foo':'bar'}]")
83
        [1, 2, (3, 4), {'foo': 'bar'}]
84
        >>> const("[1]+[2]")
85
        Traceback (most recent call last):
86
        ...
87
        ValueError: opcode BINARY_ADD not allowed
88
    """
89

90
    c = test_expr(expr, _const_codes)
1✔
91
    return eval(c)
1✔
92

93
def expr(expr):
1✔
94
    """expr(expression) -> value
95

96
    Safe Python expression evaluation
97

98
    Evaluates a string that contains an expression that only
99
    uses Python constants. This can be used to e.g. evaluate
100
    a numerical expression from an untrusted source.
101

102
    Examples:
103

104
        >>> expr("1+2")
105
        3
106
        >>> expr("[1,2]*2")
107
        [1, 2, 1, 2]
108
        >>> expr("__import__('sys').modules")
109
        Traceback (most recent call last):
110
        ...
111
        ValueError: opcode LOAD_NAME not allowed
112
    """
113

114
    c = test_expr(expr, _expr_codes)
1✔
115
    return eval(c)
1✔
116

117
def values(expr, env):
1✔
118
    """values(expression, dict) -> value
119

120
    Safe Python expression evaluation
121

122
    Evaluates a string that contains an expression that only
123
    uses Python constants and values from a supplied dictionary.
124
    This can be used to e.g. evaluate e.g. an argument to a syscall.
125

126
    Note: This is potentially unsafe if e.g. the __add__ method has side
127
          effects.
128

129
    Examples:
130

131
        >>> values("A + 4", {'A': 6})
132
        10
133
        >>> class Foo:
134
        ...    def __add__(self, other):
135
        ...        print("Firing the missiles")
136
        >>> values("A + 1", {'A': Foo()})
137
        Firing the missiles
138
        >>> values("A.x", {'A': Foo()})
139
        Traceback (most recent call last):
140
        ...
141
        ValueError: opcode LOAD_ATTR not allowed
142
    """
143

144
    # The caller might need his dictionary again
145
    env = dict(env)
1✔
146

147
    # We do not want to have built-ins set
148
    env['__builtins__'] = {}
1✔
149

150
    c = test_expr(expr, _values_codes)
1✔
151
    return eval(c, env)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc