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

zopefoundation / RestrictedPython / 3655328316

pending completion
3655328316

push

github

Michael Howitz
Fix GHA: ubuntu-latest no longer contains Python 3.6

337 of 350 branches covered (96.29%)

2439 of 2467 relevant lines covered (98.87%)

0.99 hits per line

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

97.37
/src/RestrictedPython/Guards.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE
11
#
12
##############################################################################
13

14
# This tiny set of safe builtins is extended by users of the module.
15
# AccessControl.ZopeGuards contains a large set of wrappers for builtins.
16
# DocumentTemplate.DT_UTil contains a few.
17

18
import builtins
1✔
19

20
from RestrictedPython._compat import IS_PY311_OR_GREATER
1✔
21

22

23
safe_builtins = {}
1✔
24

25
_safe_names = [
1✔
26
    '__build_class__',
27
    'None',
28
    'False',
29
    'True',
30
    'abs',
31
    'bool',
32
    'bytes',
33
    'callable',
34
    'chr',
35
    'complex',
36
    'divmod',
37
    'float',
38
    'hash',
39
    'hex',
40
    'id',
41
    'int',
42
    'isinstance',
43
    'issubclass',
44
    'len',
45
    'oct',
46
    'ord',
47
    'pow',
48
    'range',
49
    'repr',
50
    'round',
51
    'slice',
52
    'sorted',
53
    'str',
54
    'tuple',
55
    'zip'
56
]
57

58
_safe_exceptions = [
1✔
59
    'ArithmeticError',
60
    'AssertionError',
61
    'AttributeError',
62
    'BaseException',
63
    'BufferError',
64
    'BytesWarning',
65
    'DeprecationWarning',
66
    'EOFError',
67
    'EnvironmentError',
68
    'Exception',
69
    'FloatingPointError',
70
    'FutureWarning',
71
    'GeneratorExit',
72
    'IOError',
73
    'ImportError',
74
    'ImportWarning',
75
    'IndentationError',
76
    'IndexError',
77
    'KeyError',
78
    'KeyboardInterrupt',
79
    'LookupError',
80
    'MemoryError',
81
    'NameError',
82
    'NotImplementedError',
83
    'OSError',
84
    'OverflowError',
85
    'PendingDeprecationWarning',
86
    'ReferenceError',
87
    'RuntimeError',
88
    'RuntimeWarning',
89
    'StopIteration',
90
    'SyntaxError',
91
    'SyntaxWarning',
92
    'SystemError',
93
    'SystemExit',
94
    'TabError',
95
    'TypeError',
96
    'UnboundLocalError',
97
    'UnicodeDecodeError',
98
    'UnicodeEncodeError',
99
    'UnicodeError',
100
    'UnicodeTranslateError',
101
    'UnicodeWarning',
102
    'UserWarning',
103
    'ValueError',
104
    'Warning',
105
    'ZeroDivisionError',
106
]
107

108
if IS_PY311_OR_GREATER:
1!
109
    _safe_exceptions.append("ExceptionGroup")
×
110

111
for name in _safe_names:
1✔
112
    safe_builtins[name] = getattr(builtins, name)
1✔
113

114
for name in _safe_exceptions:
1✔
115
    safe_builtins[name] = getattr(builtins, name)
1✔
116

117

118
# Wrappers provided by this module:
119
# delattr
120
# setattr
121

122
# Wrappers provided by ZopeGuards:
123
# __import__
124
# apply
125
# dict
126
# enumerate
127
# filter
128
# getattr
129
# hasattr
130
# iter
131
# list
132
# map
133
# max
134
# min
135
# sum
136
# all
137
# any
138

139
# Builtins that are intentionally disabled
140
# compile   - don't let them produce new code
141
# dir       - a general purpose introspector, probably hard to wrap
142
# execfile  - no direct I/O
143
# file      - no direct I/O
144
# globals   - uncontrolled namespace access
145
# input     - no direct I/O
146
# locals    - uncontrolled namespace access
147
# open      - no direct I/O
148
# raw_input - no direct I/O
149
# vars      - uncontrolled namespace access
150

151
# There are several strings that describe Python.  I think there's no
152
# point to including these, although they are obviously safe:
153
# copyright, credits, exit, help, license, quit
154

155
# Not provided anywhere.  Do something about these?  Several are
156
# related to new-style classes, which we are too scared of to support
157
# <0.3 wink>.  coerce, buffer, and reload are esoteric enough that no
158
# one should care.
159

160
# buffer
161
# bytearray
162
# classmethod
163
# coerce
164
# eval
165
# intern
166
# memoryview
167
# object
168
# property
169
# reload
170
# staticmethod
171
# super
172
# type
173

174

175
def _write_wrapper():
1✔
176
    # Construct the write wrapper class
177
    def _handler(secattr, error_msg):
1✔
178
        # Make a class method.
179
        def handler(self, *args):
1✔
180
            try:
1✔
181
                f = getattr(self.ob, secattr)
1✔
182
            except AttributeError:
1✔
183
                raise TypeError(error_msg)
1✔
184
            f(*args)
1✔
185
        return handler
1✔
186

187
    class Wrapper:
1✔
188
        def __init__(self, ob):
1✔
189
            self.__dict__['ob'] = ob
1✔
190

191
        __setitem__ = _handler(
1✔
192
            '__guarded_setitem__',
193
            'object does not support item or slice assignment')
194

195
        __delitem__ = _handler(
1✔
196
            '__guarded_delitem__',
197
            'object does not support item or slice assignment')
198

199
        __setattr__ = _handler(
1✔
200
            '__guarded_setattr__',
201
            'attribute-less object (assign or del)')
202

203
        __delattr__ = _handler(
1✔
204
            '__guarded_delattr__',
205
            'attribute-less object (assign or del)')
206
    return Wrapper
1✔
207

208

209
def _full_write_guard():
1✔
210
    # Nested scope abuse!
211
    # safetypes and Wrapper variables are used by guard()
212
    safetypes = {dict, list}
1✔
213
    Wrapper = _write_wrapper()
1✔
214

215
    def guard(ob):
1✔
216
        # Don't bother wrapping simple types, or objects that claim to
217
        # handle their own write security.
218
        if type(ob) in safetypes or hasattr(ob, '_guarded_writes'):
1✔
219
            return ob
1✔
220
        # Hand the object to the Wrapper instance, then return the instance.
221
        return Wrapper(ob)
1✔
222
    return guard
1✔
223

224

225
full_write_guard = _full_write_guard()
1✔
226

227

228
def guarded_setattr(object, name, value):
1✔
229
    setattr(full_write_guard(object), name, value)
1✔
230

231

232
safe_builtins['setattr'] = guarded_setattr
1✔
233

234

235
def guarded_delattr(object, name):
1✔
236
    delattr(full_write_guard(object), name)
1✔
237

238

239
safe_builtins['delattr'] = guarded_delattr
1✔
240

241

242
def safer_getattr(object, name, default=None, getattr=getattr):
1✔
243
    """Getattr implementation which prevents using format on string objects.
244

245
    format() is considered harmful:
246
    http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/
247

248
    """
249
    if isinstance(object, str) and name == 'format':
1✔
250
        raise NotImplementedError(
251
            'Using format() on a %s is not safe.' % object.__class__.__name__)
252
    if name.startswith('_'):
1✔
253
        raise AttributeError(
1✔
254
            '"{name}" is an invalid attribute name because it '
255
            'starts with "_"'.format(name=name)
256
        )
257
    return getattr(object, name, default)
1✔
258

259

260
safe_builtins['_getattr_'] = safer_getattr
1✔
261

262

263
def guarded_iter_unpack_sequence(it, spec, _getiter_):
1✔
264
    """Protect sequence unpacking of targets in a 'for loop'.
265

266
    The target of a for loop could be a sequence.
267
    For example "for a, b in it"
268
    => Each object from the iterator needs guarded sequence unpacking.
269
    """
270
    # The iteration itself needs to be protected as well.
271
    for ob in _getiter_(it):
1✔
272
        yield guarded_unpack_sequence(ob, spec, _getiter_)
1✔
273

274

275
def guarded_unpack_sequence(it, spec, _getiter_):
1✔
276
    """Protect nested sequence unpacking.
277

278
    Protect the unpacking of 'it' by wrapping it with '_getiter_'.
279
    Furthermore for each child element, defined by spec,
280
    guarded_unpack_sequence is called again.
281

282
    Have a look at transformer.py 'gen_unpack_spec' for a more detailed
283
    explanation.
284
    """
285
    # Do the guarded unpacking of the sequence.
286
    ret = list(_getiter_(it))
1✔
287

288
    # If the sequence is shorter then expected the interpreter will raise
289
    # 'ValueError: need more than X value to unpack' anyway
290
    # => No childs are unpacked => nothing to protect.
291
    if len(ret) < spec['min_len']:
1✔
292
        return ret
1✔
293

294
    # For all child elements do the guarded unpacking again.
295
    for (idx, child_spec) in spec['childs']:
1✔
296
        ret[idx] = guarded_unpack_sequence(ret[idx], child_spec, _getiter_)
1✔
297

298
    return ret
1✔
299

300

301
safe_globals = {'__builtins__': safe_builtins}
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