Skip to content

Commit

Permalink
- Add some tests for features new in 2.5 and 2.6
Browse files Browse the repository at this point in the history
  • Loading branch information
sidnei committed Oct 13, 2008
1 parent b11ecfe commit 674c2ba
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 11 deletions.
2 changes: 2 additions & 0 deletions src/RestrictedPython/Guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
# max
# min
# sum
# all
# any

# Builtins that are intentionally disabled
# compile - don't let them produce new code
Expand Down
1 change: 0 additions & 1 deletion src/RestrictedPython/RestrictionMutator.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,3 @@ def visitImport(self, node, walker):
if asname:
self.checkName(node, asname)
return node

34 changes: 34 additions & 0 deletions src/RestrictedPython/tests/before_and_after25.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
##############################################################################
#
# Copyright (c) 2008 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Restricted Python transformation examples
This module contains pairs of functions. Each pair has a before and an
after function. The after function shows the source code equivalent
of the before function after it has been modified by the restricted
compiler.
These examples are actually used in the testRestrictions.py
checkBeforeAndAfter() unit tests, which verifies that the restricted compiler
actually produces the same output as would be output by the normal compiler
for the after function.
$Id: before_and_after24.py 76322 2007-06-04 17:40:03Z philikon $
"""

def simple_ternary_if_before():
x.y = y.z if y.z else y.x

def simple_ternary_if_after():
_write_(x).y = _getattr_(y, 'z') if _getattr_(y, 'z') else _getattr_(y, 'x')

51 changes: 51 additions & 0 deletions src/RestrictedPython/tests/before_and_after26.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
##############################################################################
#
# Copyright (c) 2008 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Restricted Python transformation examples
This module contains pairs of functions. Each pair has a before and an
after function. The after function shows the source code equivalent
of the before function after it has been modified by the restricted
compiler.
These examples are actually used in the testRestrictions.py
checkBeforeAndAfter() unit tests, which verifies that the restricted compiler
actually produces the same output as would be output by the normal compiler
for the after function.
$Id: before_and_after24.py 76322 2007-06-04 17:40:03Z philikon $
"""

def simple_context_before():
with whatever as x:
x.y = z

def simple_context_after():
with whatever as x:
_write_(x).y = z

def simple_context_assign_attr_before():
with whatever as x.y:
x.y = z

def simple_context_assign_attr_after():
with whatever as _write_(x).y:
_write_(x).y = z

def simple_context_load_attr_before():
with whatever.w as z:
x.y = z

def simple_context_load_attr_after():
with _getattr_(whatever, 'w') as z:
_write_(x).y = z
7 changes: 7 additions & 0 deletions src/RestrictedPython/tests/security_in_syntax26.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# These are all supposed to raise a SyntaxError when using
# compile_restricted() but not when using compile().
# Each function in this module is compiled using compile_restricted().

def with_as_bad_name():
with x as _leading_underscore:
pass
30 changes: 24 additions & 6 deletions src/RestrictedPython/tests/testRestrictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ def checkDenied(self):
self.fail('%s() did not trip security' % k)

def checkSyntaxSecurity(self):
self._checkSyntaxSecurity('security_in_syntax.py')
if sys.version_info >= (2, 6):
self._checkSyntaxSecurity('security_in_syntax26.py')

def _checkSyntaxSecurity(self, mod_name):
# Ensures that each of the functions in security_in_syntax.py
# throws a SyntaxError when using compile_restricted.
fn = os.path.join(_HERE, 'security_in_syntax.py')
Expand Down Expand Up @@ -358,25 +363,23 @@ def checkBeforeAndAfter(self):
rm.compile()
verify.verify(rm.getCode())

if sys.version_info[:2] >= (2, 4):
def checkBeforeAndAfter24(self):
def _checkBeforeAndAfter(self, mod):
from RestrictedPython.RCompile import RModule
from RestrictedPython.tests import before_and_after24
from compiler import parse

defre = re.compile(r'def ([_A-Za-z0-9]+)_(after|before)\(')

beforel = [name for name in before_and_after24.__dict__
beforel = [name for name in mod.__dict__
if name.endswith("_before")]

for name in beforel:
before = getattr(before_and_after24, name)
before = getattr(mod, name)
before_src = get_source(before)
before_src = re.sub(defre, r'def \1(', before_src)
rm = RModule(before_src, '')
tree_before = rm._get_tree()

after = getattr(before_and_after24, name[:-6]+'after')
after = getattr(mod, name[:-6]+'after')
after_src = get_source(after)
after_src = re.sub(defre, r'def \1(', after_src)
tree_after = parse(after_src)
Expand All @@ -386,6 +389,21 @@ def checkBeforeAndAfter24(self):
rm.compile()
verify.verify(rm.getCode())

if sys.version_info[:2] >= (2, 4):
def checkBeforeAndAfter24(self):
from RestrictedPython.tests import before_and_after24
self._checkBeforeAndAfter(before_and_after24)

if sys.version_info[:2] >= (2, 5):
def checkBeforeAndAfter25(self):
from RestrictedPython.tests import before_and_after25
self._checkBeforeAndAfter(before_and_after25)

if sys.version_info[:2] >= (2, 6):
def checkBeforeAndAfter26(self):
from RestrictedPython.tests import before_and_after26
self._checkBeforeAndAfter(before_and_after26)

def _compile_file(self, name):
path = os.path.join(_HERE, name)
f = open(path, "r")
Expand Down
36 changes: 32 additions & 4 deletions src/RestrictedPython/tests/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def _verifycode(code):
line = code.co_firstlineno
# keep a window of the last three opcodes, with the most recent first
window = (None, None, None)
with_context = (None, None)

for op in disassemble(code):
if op.line is not None:
Expand All @@ -54,11 +55,28 @@ def _verifycode(code):
# All the user code that generates LOAD_ATTR should be
# rewritten, but the code generated for a list comp
# includes a LOAD_ATTR to extract the append method.
if not (op.arg == "append" and
window[0].opname == "DUP_TOP" and
window[1].opname == "BUILD_LIST"):
# Another exception is the new-in-Python 2.6 'context
# managers', which do a LOAD_ATTR for __exit__ and
# __enter__.
if op.arg == "__exit__":
with_context = (op, with_context[1])
elif op.arg == "__enter__":
with_context = (with_context[0], op)
elif not ((op.arg == "__enter__" and
window[0].opname == "ROT_TWO" and
window[1].opname == "DUP_TOP") or
(op.arg == "append" and
window[0].opname == "DUP_TOP" and
window[1].opname == "BUILD_LIST")):
raise ValueError("direct attribute access %s: %s, %s:%d"
% (op.opname, op.arg, co.co_filename, line))
% (op.opname, op.arg, code.co_filename, line))
if op.opname in ("WITH_CLEANUP"):
# Here we check if the LOAD_ATTR for __exit__ and
# __enter__ were part of a 'with' statement by checking
# for the 'WITH_CLEANUP' bytecode. If one is seen, we
# clear the with_context variable and let it go. The
# access was safe.
with_context = (None, None)
if op.opname in ("STORE_ATTR", "DEL_ATTR"):
if not (window[0].opname == "CALL_FUNCTION" and
window[2].opname == "LOAD_GLOBAL" and
Expand Down Expand Up @@ -90,6 +108,16 @@ def _verifycode(code):

window = (op,) + window[:2]

if not with_context == (None, None):
# An access to __enter__ and __exit__ was performed but not as
# part of a 'with' statement. This is not allowed.
for op in with_context:
if op is not None:
if op.line is not None:
line = op.line
raise ValueError("direct attribute access %s: %s, %s:%d"
% (op.opname, op.arg, code.co_filename, line))

class Op(object):
__slots__ = (
"opname", # string, name of the opcode
Expand Down

0 comments on commit 674c2ba

Please sign in to comment.