1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
|
import platform
import types
import pytest
from RestrictedPython import CompileResult
from RestrictedPython import compile_restricted
from RestrictedPython import compile_restricted_eval
from RestrictedPython import compile_restricted_exec
from RestrictedPython import compile_restricted_single
from RestrictedPython._compat import IS_PY310_OR_GREATER
from RestrictedPython._compat import IS_PY311_OR_GREATER
from tests.helper import restricted_eval
def test_compile__compile_restricted_invalid_code_input():
with pytest.raises(TypeError):
compile_restricted(object(), '<string>', 'exec')
with pytest.raises(TypeError):
compile_restricted(object(), '<string>', 'eval')
with pytest.raises(TypeError):
compile_restricted(object(), '<string>', 'single')
def test_compile__compile_restricted_invalid_policy_input():
with pytest.raises(TypeError):
compile_restricted("pass", '<string>', 'exec', policy=object)
def test_compile__compile_restricted_invalid_mode_input():
with pytest.raises(TypeError):
compile_restricted("pass", '<string>', 'invalid')
INVALID_ASSINGMENT = """
1 = 2
"""
def test_compile__invalid_syntax():
with pytest.raises(SyntaxError) as err:
compile_restricted(INVALID_ASSINGMENT, '<string>', 'exec')
if IS_PY310_OR_GREATER:
assert "SyntaxError: cannot assign to literal here." in str(err.value)
else:
assert "cannot assign to literal at statement:" in str(err.value)
def test_compile__compile_restricted_exec__1():
"""It returns a CompileResult on success."""
result = compile_restricted_exec('a = 42')
assert result.__class__ == CompileResult
assert result.errors == ()
assert result.warnings == []
assert result.used_names == {}
glob = {}
exec(result.code, glob)
assert glob['a'] == 42
def test_compile__compile_restricted_exec__2():
"""It compiles without restrictions if there is no policy."""
result = compile_restricted_exec('_a = 42', policy=None)
assert result.errors == ()
assert result.warnings == []
assert result.used_names == {}
glob = {}
exec(result.code, glob)
assert glob['_a'] == 42
def test_compile__compile_restricted_exec__3():
"""It returns a tuple of errors if the code is not allowed.
There is no code in this case.
"""
result = compile_restricted_exec('_a = 42\n_b = 43')
errors = (
'Line 1: "_a" is an invalid variable name because it starts with "_"',
'Line 2: "_b" is an invalid variable name because it starts with "_"')
assert result.errors == errors
assert result.warnings == []
assert result.used_names == {}
assert result.code is None
def test_compile__compile_restricted_exec__4():
"""It does not return code on a SyntaxError."""
result = compile_restricted_exec('asdf|')
assert result.code is None
assert result.warnings == []
assert result.used_names == {}
assert result.errors == (
"Line 1: SyntaxError: invalid syntax at statement: 'asdf|'",)
def test_compile__compile_restricted_exec__5():
"""It does not return code if the code contains a NULL byte."""
result = compile_restricted_exec('a = 5\x00')
assert result.code is None
assert result.warnings == []
assert result.used_names == {}
if IS_PY311_OR_GREATER:
assert result.errors == (
'Line None: SyntaxError: source code string cannot contain null'
' bytes at statement: None',)
else:
assert result.errors == (
'source code string cannot contain null bytes',)
EXEC_STATEMENT = """\
def no_exec():
exec 'q = 1'
"""
def test_compile__compile_restricted_exec__10():
"""It is a SyntaxError to use the `exec` statement."""
result = compile_restricted_exec(EXEC_STATEMENT)
if IS_PY310_OR_GREATER:
assert (
'Line 2: SyntaxError: Missing parentheses in call to \'exec\'. Did'
' you mean exec(...)? at statement: "exec \'q = 1\'"',
) == result.errors
else:
assert (
'Line 2: SyntaxError: Missing parentheses in call to \'exec\' at'
' statement: "exec \'q = 1\'"',) == result.errors
FUNCTION_DEF = """\
def a():
pass
"""
def test_compile__compile_restricted_eval__1():
"""It compiles code as an Expression.
Function definitions are not allowed in Expressions.
"""
result = compile_restricted_eval(FUNCTION_DEF)
assert result.errors == (
"Line 1: SyntaxError: invalid syntax at statement: 'def a():'",)
def test_compile__compile_restricted_eval__2():
"""It compiles code as an Expression."""
assert restricted_eval('4 * 6') == 24
def test_compile__compile_restricted_eval__used_names():
result = compile_restricted_eval("a + b + func(x)")
assert result.errors == ()
assert result.warnings == []
assert result.used_names == {'a': True, 'b': True, 'x': True, 'func': True}
def test_compile__compile_restricted_single__1():
"""It compiles code as an Interactive."""
result = compile_restricted_single('x = 4 * 6')
assert result.errors == ()
assert result.warnings == []
assert result.code is not None
locals = {}
exec(result.code, {}, locals)
assert locals["x"] == 24
def test_compile__compile_restricted__2():
"""It compiles code as an Interactive."""
code = compile_restricted('x = 4 * 6', filename="<string>", mode="single")
locals = {}
exec(code, {}, locals)
assert locals["x"] == 24
PRINT_EXAMPLE = """
def a():
print('Hello World!')
"""
def test_compile_restricted():
"""It emits Python warnings.
For actual tests for print statement see: test_print_stmt.py
"""
with pytest.warns(SyntaxWarning) as record:
result = compile_restricted(PRINT_EXAMPLE, '<string>', 'exec')
assert isinstance(result, types.CodeType)
assert len(record) == 1
assert record[0].message.args[0] == \
"Line 2: Prints, but never reads 'printed' variable."
EVAL_EXAMPLE = """
def a():
eval('2 + 2')
"""
def test_compile_restricted_eval():
"""This test checks compile_restricted itself if that raise Python errors.
"""
with pytest.raises(SyntaxError,
match="Line 3: Eval calls are not allowed."):
compile_restricted(EVAL_EXAMPLE, '<string>', 'exec')
def test_compile___compile_restricted_mode__1(recwarn, mocker):
"""It warns when using another Python implementation than CPython."""
if platform.python_implementation() == 'CPython': # pragma: no cover
# Using CPython we have to fake the check:
mocker.patch('RestrictedPython.compile.IS_CPYTHON', new=False)
compile_restricted('42')
assert len(recwarn) == 1
w = recwarn.pop()
assert w.category == RuntimeWarning
assert str(w.message) == str(
'RestrictedPython is only supported on CPython: use on other Python '
'implementations may create security issues.'
)
@pytest.mark.skipif(
platform.python_implementation() == 'CPython',
reason='Warning only present if not CPython.')
def test_compile_CPython_warning(recwarn, mocker): # pragma: no cover
"""It warns when using another Python implementation than CPython."""
assert platform.python_implementation() != 'CPython'
with pytest.warns(RuntimeWarning) as record:
compile_restricted('42')
assert len(record) == 1
assert str(record[0].message) == str(
'RestrictedPython is only supported on CPython: use on other Python '
'implementations may create security issues.'
)
|