Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
R
RestrictedPython-3.6.0
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
RestrictedPython-3.6.0
Commits
33485e29
Commit
33485e29
authored
Dec 09, 2016
by
Boxiang Sun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New RestrictedPython implementation
parent
19a5b07c
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
829 additions
and
449 deletions
+829
-449
src/RestrictedPython/Eval.py
src/RestrictedPython/Eval.py
+10
-11
src/RestrictedPython/MutatingWalker.py
src/RestrictedPython/MutatingWalker.py
+69
-53
src/RestrictedPython/RCompile.py
src/RestrictedPython/RCompile.py
+170
-117
src/RestrictedPython/RestrictionMutator.py
src/RestrictedPython/RestrictionMutator.py
+465
-168
src/RestrictedPython/tests/before_and_after.py
src/RestrictedPython/tests/before_and_after.py
+7
-7
src/RestrictedPython/tests/class.py
src/RestrictedPython/tests/class.py
+4
-2
src/RestrictedPython/tests/restricted_module.py
src/RestrictedPython/tests/restricted_module.py
+2
-2
src/RestrictedPython/tests/security_in_syntax.py
src/RestrictedPython/tests/security_in_syntax.py
+3
-3
src/RestrictedPython/tests/testCompile.py
src/RestrictedPython/tests/testCompile.py
+9
-4
src/RestrictedPython/tests/testREADME.py
src/RestrictedPython/tests/testREADME.py
+3
-0
src/RestrictedPython/tests/testRestrictions.py
src/RestrictedPython/tests/testRestrictions.py
+47
-42
src/RestrictedPython/tests/unpack.py
src/RestrictedPython/tests/unpack.py
+40
-40
No files found.
src/RestrictedPython/Eval.py
View file @
33485e29
...
@@ -56,17 +56,16 @@ class RestrictionCapableEval:
...
@@ -56,17 +56,16 @@ class RestrictionCapableEval:
if
PROFILE
:
if
PROFILE
:
from
time
import
clock
from
time
import
clock
start
=
clock
()
start
=
clock
()
# Pyston change: do nothing
co
,
err
,
warn
,
used
=
compile_restricted_eval
(
# co, err, warn, used = compile_restricted_eval(
self
.
expr
,
'<string>'
)
# self.expr, '<string>')
if
PROFILE
:
# if PROFILE:
end
=
clock
()
# end = clock()
print
'prepRestrictedCode: %d ms for %s'
%
(
# print 'prepRestrictedCode: %d ms for %s' % (
(
end
-
start
)
*
1000
,
`self.expr`
)
# (end - start) * 1000, `self.expr`)
if
err
:
# if err:
raise
SyntaxError
,
err
[
0
]
# raise SyntaxError, err[0]
self
.
used
=
tuple
(
used
.
keys
())
# self.used = tuple(used.keys())
self
.
rcode
=
co
# self.rcode = co
def
prepUnrestrictedCode
(
self
):
def
prepUnrestrictedCode
(
self
):
if
self
.
ucode
is
None
:
if
self
.
ucode
is
None
:
...
...
src/RestrictedPython/MutatingWalker.py
View file @
33485e29
...
@@ -14,61 +14,77 @@
...
@@ -14,61 +14,77 @@
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
# from SelectCompiler import ast
# from SelectCompiler import ast
import
ast
ListType
=
type
([])
ListType
=
type
([])
TupleType
=
type
(())
TupleType
=
type
(())
SequenceTypes
=
(
ListType
,
TupleType
)
SequenceTypes
=
(
ListType
,
TupleType
)
# class MutatingWalker:
class
MutatingWalker
:
#
# def __init__(self, visitor):
def
__init__
(
self
,
visitor
):
# self.visitor = visitor
self
.
visitor
=
visitor
# self._cache = {}
self
.
_cache
=
{}
#
# def defaultVisitNode(self, node, walker=None, exclude=None):
def
defaultVisitNode
(
self
,
node
,
walker
=
None
,
exclude
=
None
,
exclude_node
=
None
):
# for name, child in node.__dict__.items():
for
child
in
ast
.
walk
(
node
):
# if exclude is not None and name in exclude:
if
exclude_node
is
not
None
and
isinstance
(
child
,
exclude_node
):
# continue
continue
# v = self.dispatchObject(child)
klass
=
child
.
__class__
# if v is not child:
meth
=
self
.
_cache
.
get
(
klass
,
None
)
# # Replace the node.
if
meth
is
None
:
# node.__dict__[name] = v
className
=
klass
.
__name__
# return node
meth
=
getattr
(
self
.
visitor
,
'visit'
+
className
,
#
self
.
defaultVisitNode
)
# def visitSequence(self, seq):
self
.
_cache
[
klass
]
=
meth
# res = seq
return
meth
(
node
,
self
)
# for idx in range(len(seq)):
# for name, child in node.__dict__.items():
# child = seq[idx]
# if exclude is not None and name in exclude:
# v = self.dispatchObject(child)
# continue
# if v is not child:
# v = self.dispatchObject(child, exclude_node)
# # Change the sequence.
# if v is not child:
# if type(res) is ListType:
# # Replace the node.
# res[idx : idx + 1] = [v]
# node.__dict__[name] = v
# else:
# return node
# res = res[:idx] + (v,) + res[idx + 1:]
#
# return res
# def visitSequence(self, seq, exclude_node=None):
#
# res = seq
# def dispatchObject(self, ob):
# for idx in range(len(seq)):
# '''
# child = seq[idx]
# Expected to return either ob or something that will take
# v = self.dispatchObject(child, exclude_node)
# its place.
# if v is not child:
# '''
# # Change the sequence.
# if isinstance(ob, ast.Node):
# if type(res) is ListType:
# return self.dispatchNode(ob)
# res[idx : idx + 1] = [v]
# elif type(ob) in SequenceTypes:
# else:
# return self.visitSequence(ob)
# res = res[:idx] + (v,) + res[idx + 1:]
# else:
# return res
# return ob
#
#
# def dispatchObject(self, ob, exclude_node=None):
# def dispatchNode(self, node):
# '''
# klass = node.__class__
# Expected to return either ob or something that will take
# meth = self._cache.get(klass, None)
# its place.
# if meth is None:
# '''
# className = klass.__name__
# if isinstance(ob, ast.AST):
# meth = getattr(self.visitor, 'visit' + className,
# return self.dispatchNode(ob, exclude_node)
# self.defaultVisitNode)
# elif type(ob) in SequenceTypes:
# self._cache[klass] = meth
# return self.visitSequence(ob)
# return meth(node, self)
# else:
#
# return ob
# def walk(tree, visitor):
#
# return MutatingWalker(visitor).dispatchNode(tree)
# def dispatchNode(self, node, exclude_node=None):
# for child in ast.walk(node):
# if exclude_node is not None and isinstance(child, exclude_node):
# continue
# klass = child.__class__
# meth = self._cache.get(klass, None)
# if meth is None:
# className = klass.__name__
# meth = getattr(self.visitor, 'visit' + className,
# self.defaultVisitNode)
# self._cache[klass] = meth
# return meth(node, self)
def
walk
(
tree
,
visitor
):
return
# return MutatingWalker(visitor).defaultVisitNode(tree)
src/RestrictedPython/RCompile.py
View file @
33485e29
...
@@ -20,61 +20,90 @@ __version__='$Revision: 1.6 $'[11:-2]
...
@@ -20,61 +20,90 @@ __version__='$Revision: 1.6 $'[11:-2]
# from compiler.pycodegen import AbstractCompileMode, Expression, \
# from compiler.pycodegen import AbstractCompileMode, Expression, \
# Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp
# Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp
#
#
# import MutatingWalker
# from RestrictionMutator import RestrictionMutator
# The AbstractCompileMode
#
# The compiler.pycodegen.Expression is just a subclass of AbstractCompileMode.
#
import
ast
# def niceParse(source, filename, mode):
import
MutatingWalker
# if isinstance(source, unicode):
from
RestrictionMutator
import
RestrictionTransformer
# # Use the utf-8-sig BOM so the compiler
from
ast
import
parse
# # detects this as a UTF-8 encoded string.
# source = '\xef\xbb\xbf' + source.encode('utf-8')
# try:
def
niceParse
(
source
,
filename
,
mode
):
# return parse(source, mode)
if
isinstance
(
source
,
unicode
):
# except:
# Use the utf-8-sig BOM so the compiler
# # Try to make a clean error message using
# detects this as a UTF-8 encoded string.
# # the builtin Python compiler.
source
=
'
\
xef
\
xbb
\
xbf
'
+
source
.
encode
(
'utf-8'
)
# try:
try
:
# compile(source, filename, mode)
return
parse
(
source
,
mode
=
mode
)
# except SyntaxError:
except
:
# raise
# Try to make a clean error message using
# # Some other error occurred.
# the builtin Python compiler.
# raise
try
:
#
compile
(
source
,
filename
,
mode
)
except
SyntaxError
:
raise
# Some other error occurred.
raise
# Pyston change: the AbstractCompileMode is in Python/Lib/compiler/pycodegen
# it is just a same class like RestructedCompileMode, nothing special to inheritate.
# class RestrictedCompileMode(AbstractCompileMode):
# class RestrictedCompileMode(AbstractCompileMode):
class
RestrictedCompileMode
(
object
):
# """Abstract base class for hooking up custom CodeGenerator."""
# """Abstract base class for hooking up custom CodeGenerator."""
# # See concrete subclasses below.
# # See concrete subclasses below.
#
#
# def __init__(self, source, filename):
def
__init__
(
self
,
source
,
filename
,
mode
=
'exec'
):
# if source:
if
source
:
# source = '\n'.join(source.splitlines()) + '\n'
source
=
'
\
n
'
.
join
(
source
.
splitlines
())
+
'
\
n
'
# self.rm = RestrictionMutator()
self
.
rt
=
RestrictionTransformer
()
# AbstractCompileMode.__init__(self, source, filename)
self
.
source
=
source
#
self
.
filename
=
filename
# def parse(self):
self
.
code
=
None
# return niceParse(self.source, self.filename, self.mode)
self
.
mode
=
mode
#
# AbstractCompileMode.__init__(self, source, filename)
# def _get_tree(self):
# tree = self.parse()
def
parse
(
self
):
# MutatingWalker.walk(tree, self.rm)
# print("Sourceeeeeeeeeeeeeeeeeeeee")
# if self.rm.errors:
# print(self.source)
# raise SyntaxError, self.rm.errors[0]
return
niceParse
(
self
.
source
,
self
.
filename
,
self
.
mode
)
# misc.set_filename(self.filename, tree)
# syntax.check(tree)
def
_get_tree
(
self
):
# return tree
tree
=
self
.
parse
()
#
# MutatingWalker.walk(tree, self.rt)
# def compile(self):
# print("Treeeeeeeeeeeeeeeeeee")
# tree = self._get_tree()
# print(ast.dump(tree))
# gen = self.CodeGeneratorClass(tree)
self
.
rt
.
visit
(
tree
)
# self.code = gen.getCode()
# print(ast.dump(tree))
#
# print(self.rt.errors)
#
if
self
.
rt
.
errors
:
# def compileAndTuplize(gen):
raise
SyntaxError
,
self
.
rt
.
errors
[
0
]
# try:
# misc.set_filename(self.filename, tree)
# gen.compile()
# syntax.check(tree)
# except SyntaxError, v:
return
tree
# return None, (str(v),), gen.rm.warnings, gen.rm.used_names
# return gen.getCode(), (), gen.rm.warnings, gen.rm.used_names
def
compile
(
self
):
tree
=
self
.
_get_tree
()
# gen = self.CodeGeneratorClass(tree)
# Compile it directly????
# print(ast.dump(tree))
# for node in ast.walk(tree):
# print(type(node))
# print(node.lineno)
# print(node.col_offset)
self
.
code
=
compile
(
tree
,
self
.
filename
,
self
.
mode
)
# self.code = gen.getCode()
def
getCode
(
self
):
return
self
.
code
def
compileAndTuplize
(
gen
):
try
:
gen
.
compile
()
except
SyntaxError
,
v
:
return
None
,
(
str
(
v
),),
gen
.
rt
.
warnings
,
gen
.
rt
.
used_names
return
gen
.
getCode
(),
(),
gen
.
rt
.
warnings
,
gen
.
rt
.
used_names
def
compile_restricted_function
(
p
,
body
,
name
,
filename
,
globalize
=
None
):
def
compile_restricted_function
(
p
,
body
,
name
,
filename
,
globalize
=
None
):
"""Compiles a restricted code object for a function.
"""Compiles a restricted code object for a function.
...
@@ -87,25 +116,30 @@ def compile_restricted_function(p, body, name, filename, globalize=None):
...
@@ -87,25 +116,30 @@ def compile_restricted_function(p, body, name, filename, globalize=None):
treated as globals (code is generated as if each name in the list
treated as globals (code is generated as if each name in the list
appeared in a global statement at the top of the function).
appeared in a global statement at the top of the function).
"""
"""
# gen = RFunction(p, body, name, filename, globalize)
gen
=
RFunction
(
p
,
body
,
name
,
filename
,
globalize
)
# return compileAndTuplize(gen)
return
compileAndTuplize
(
gen
)
return
None
def
compile_restricted_exec
(
s
,
filename
=
'<string>'
):
def
compile_restricted_exec
(
s
ource
,
filename
=
'<string>'
):
"""Compiles a restricted code suite."""
"""Compiles a restricted code suite."""
# gen = RModule(s, filename)
# gen = RModule(s, filename)
# return compileAndTuplize(gen
)
gen
=
RestrictedCompileMode
(
source
,
filename
,
'exec'
)
return
None
return
compileAndTuplize
(
gen
)
def
compile_restricted_eval
(
s
,
filename
=
'<string>'
):
def
compile_restricted_eval
(
s
ource
,
filename
=
'<string>'
):
"""Compiles a restricted expression."""
"""Compiles a restricted expression."""
# gen = RExpression(s, filename)
# gen = RExpression(s, filename)
# return compileAndTuplize(gen)
gen
=
RestrictedCompileMode
(
source
,
filename
,
'eval'
)
return
compileAndTuplize
(
gen
)
# return compile(s, filename, 'eval'), (), [], {}
# return compile(s, filename, 'eval'), (), [], {}
return
None
#
return None
def
compile_restricted
(
source
,
filename
,
mode
):
def
compile_restricted
(
source
,
filename
,
mode
):
"""Replacement for the builtin compile() function."""
"""Replacement for the builtin compile() function."""
# print("We are here!!!!!!!!!!!!!!!!!!!!")
if
mode
not
in
(
'single'
,
'exec'
,
'eval'
):
raise
ValueError
(
"compile_restricted() 3rd arg must be 'exec' or "
"'eval' or 'single'"
)
gen
=
RestrictedCompileMode
(
source
,
filename
,
mode
)
# if mode == "single":
# if mode == "single":
# gen = RInteractive(source, filename)
# gen = RInteractive(source, filename)
# elif mode == "exec":
# elif mode == "exec":
...
@@ -115,9 +149,8 @@ def compile_restricted(source, filename, mode):
...
@@ -115,9 +149,8 @@ def compile_restricted(source, filename, mode):
# else:
# else:
# raise ValueError("compile_restricted() 3rd arg must be 'exec' or "
# raise ValueError("compile_restricted() 3rd arg must be 'exec' or "
# "'eval' or 'single'")
# "'eval' or 'single'")
# gen.compile()
gen
.
compile
()
# return gen.getCode()
return
gen
.
getCode
()
return
None
# class RestrictedCodeGenerator:
# class RestrictedCodeGenerator:
# """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes.
# """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes.
...
@@ -190,60 +223,80 @@ def compile_restricted(source, filename, mode):
...
@@ -190,60 +223,80 @@ def compile_restricted(source, filename, mode):
# def initClass(self):
# def initClass(self):
# ModuleCodeGenerator.initClass(self)
# ModuleCodeGenerator.initClass(self)
# self.__class__.FunctionGen = RestrictedFunctionCodeGenerator
# self.__class__.FunctionGen = RestrictedFunctionCodeGenerator
#
#
# # These subclasses work around the definition of stub compile and mode
# These subclasses work around the definition of stub compile and mode
# # attributes in the common base class AbstractCompileMode. If it
# attributes in the common base class AbstractCompileMode. If it
# # didn't define new attributes, then the stub code inherited via
# didn't define new attributes, then the stub code inherited via
# # RestrictedCompileMode would override the real definitions in
# RestrictedCompileMode would override the real definitions in
# # Expression.
# Expression.
#
class
RExpression
(
RestrictedCompileMode
):
# class RExpression(RestrictedCompileMode, Expression):
# class RExpression(RestrictedCompileMode, Expression):
# mode = "eval"
mode
=
"eval"
# CodeGeneratorClass = RestrictedExpressionCodeGenerator
# CodeGeneratorClass = RestrictedExpressionCodeGenerator
#
# CodeGeneratorClass = RestrictedCodeGenerator
class
RInteractive
(
RestrictedCompileMode
):
# class RInteractive(RestrictedCompileMode, Interactive):
# class RInteractive(RestrictedCompileMode, Interactive):
# mode = "single"
mode
=
"single"
# CodeGeneratorClass = RestrictedInteractiveCodeGenerator
# CodeGeneratorClass = RestrictedInteractiveCodeGenerator
#
# CodeGeneratorClass = RestrictedCodeGenerator
class
RModule
(
RestrictedCompileMode
):
# class RModule(RestrictedCompileMode, Module):
# class RModule(RestrictedCompileMode, Module):
# mode = "exec"
# mode = "exec"
# CodeGeneratorClass = RestrictedModuleCodeGenerator
def
__init__
(
self
,
source
,
filename
):
#
# self.source = source
# class RFunction(RModule):
# self.filename = filename
# """A restricted Python function built from parts."""
# self.code = None
#
# self.mode = 'exec'
# CodeGeneratorClass = RestrictedModuleCodeGenerator
super
(
RModule
,
self
).
__init__
(
source
,
filename
,
'exec'
)
#
# CodeGeneratorClass = RestrictedModuleCodeGenerator
# def __init__(self, p, body, name, filename, globals):
# CodeGeneratorClass = RestrictedCodeGenerator
# self.params = p
# if body:
class
RFunction
(
RModule
):
# body = '\n'.join(body.splitlines()) + '\n'
"""A restricted Python function built from parts."""
# self.body = body
# self.name = name
# CodeGeneratorClass = RestrictedCodeGenerator
# self.globals = globals or []
# CodeGeneratorClass = RestrictedModuleCodeGenerator
# RModule.__init__(self, None, filename)
#
def
__init__
(
self
,
p
,
body
,
name
,
filename
,
globals
):
# def parse(self):
self
.
params
=
p
# # Parse the parameters and body, then combine them.
if
body
:
# firstline = 'def f(%s): pass' % self.params
body
=
'
\
n
'
.
join
(
body
.
splitlines
())
+
'
\
n
'
# tree = niceParse(firstline, '<function parameters>', 'exec')
self
.
body
=
body
# f = tree.node.nodes[0]
self
.
name
=
name
# body_code = niceParse(self.body, self.filename, 'exec')
self
.
globals
=
globals
or
[]
# # Stitch the body code into the function.
RModule
.
__init__
(
self
,
None
,
filename
)
# f.code.nodes = body_code.node.nodes
# f.name = self.name
def
parse
(
self
):
# # Look for a docstring, if there are any nodes at all
# Parse the parameters and body, then combine them.
# if len(f.code.nodes) > 0:
firstline
=
'def f(%s): pass'
%
self
.
params
# stmt1 = f.code.nodes[0]
tree
=
niceParse
(
firstline
,
'<function parameters>'
,
'exec'
)
# if (isinstance(stmt1, ast.Discard) and
# f = tree.node.nodes[0]
# isinstance(stmt1.expr, ast.Const) and
f
=
tree
.
body
[
0
]
# isinstance(stmt1.expr.value, str)):
body_code
=
niceParse
(
self
.
body
,
self
.
filename
,
'exec'
)
# f.doc = stmt1.expr.value
# Stitch the body code into the function.
# # The caller may specify that certain variables are globals
f
.
body
=
body_code
.
body
# # so that they can be referenced before a local assignment.
# f.code.nodes = body_code.node.nodes
# # The only known example is the variables context, container,
f
.
name
=
self
.
name
# # script, traverse_subpath in PythonScripts.
# Look for a docstring, if there are any nodes at all
# if self.globals:
# if len(f.code.nodes) > 0:
# f.code.nodes.insert(0, ast.Global(self.globals))
if
len
(
f
.
body
)
>
0
:
# return tree
stmt1
=
f
.
body
[
0
]
# if (isinstance(stmt1, ast.Discard) and
# isinstance(stmt1.expr, ast.Const) and
# isinstance(stmt1.expr.value, str)):
# f.doc = stmt1.expr.value
if
(
isinstance
(
stmt1
,
ast
.
Expr
)
and
isinstance
(
stmt1
.
value
,
ast
.
Str
)):
f
.
__doc__
=
stmt1
.
value
.
s
# The caller may specify that certain variables are globals
# so that they can be referenced before a local assignment.
# The only known example is the variables context, container,
# script, traverse_subpath in PythonScripts.
if
self
.
globals
:
f
.
body
.
insert
(
0
,
ast
.
Global
(
self
.
globals
))
# f.code.nodes.insert(0, ast.Global(self.globals))
return
tree
src/RestrictedPython/RestrictionMutator.py
View file @
33485e29
...
@@ -19,48 +19,58 @@ code in various ways before sending it to pycodegen.
...
@@ -19,48 +19,58 @@ code in various ways before sending it to pycodegen.
$Revision: 1.13 $
$Revision: 1.13 $
"""
"""
from
SelectCompiler
import
ast
,
parse
,
OP_ASSIGN
,
OP_DELETE
,
OP_APPLY
# from SelectCompiler import ast, parse, OP_ASSIGN, OP_DELETE, OP_APPLY
import
ast
from
ast
import
parse
# These utility functions allow us to generate AST subtrees without
# These utility functions allow us to generate AST subtrees without
# line number attributes. These trees can then be inserted into other
# line number attributes. These trees can then be inserted into other
# trees without affecting line numbers shown in tracebacks, etc.
# trees without affecting line numbers shown in tracebacks, etc.
def
rmLineno
(
node
):
def
rmLineno
(
node
):
"""Strip lineno attributes from a code tree."""
"""Strip lineno attributes from a code tree."""
if
node
.
__dict__
.
has_key
(
'lineno'
):
for
child
in
ast
.
walk
(
node
):
del
node
.
lineno
if
'lineno'
in
child
.
_attributes
:
for
child
in
node
.
getChildren
():
del
child
.
lineno
if
isinstance
(
child
,
ast
.
Node
):
# if node.__dict__.has_key('lineno'):
rmLineno
(
child
)
# del node.lineno
# for child in node.getChildren():
# if isinstance(child, ast.AST):
# rmLineno(child)
def
stmtNode
(
txt
):
def
stmtNode
(
txt
):
"""Make a "clean" statement node."""
"""Make a "clean" statement node."""
node
=
parse
(
txt
).
node
.
nodes
[
0
]
# node = parse(txt).node.nodes[0]
rmLineno
(
node
)
node
=
parse
(
txt
).
body
[
0
]
# TODO: Remove the line number of nodes will cause error.
# Need to figure out why.
# rmLineno(node)
return
node
return
node
# The security checks are performed by a set of six functions that
# The security checks are performed by a set of six functions that
# must be provided by the restricted environment.
# must be provided by the restricted environment.
_apply_name
=
ast
.
Name
(
"_apply_"
)
_apply_name
=
ast
.
Name
(
"_apply_"
,
ast
.
Load
()
)
_getattr_name
=
ast
.
Name
(
"_getattr_"
)
_getattr_name
=
ast
.
Name
(
"_getattr_"
,
ast
.
Load
()
)
_getitem_name
=
ast
.
Name
(
"_getitem_"
)
_getitem_name
=
ast
.
Name
(
"_getitem_"
,
ast
.
Load
()
)
_getiter_name
=
ast
.
Name
(
"_getiter_"
)
_getiter_name
=
ast
.
Name
(
"_getiter_"
,
ast
.
Load
()
)
_print_target_name
=
ast
.
Name
(
"_print"
)
_print_target_name
=
ast
.
Name
(
"_print"
,
ast
.
Load
()
)
_write_name
=
ast
.
Name
(
"_write_"
)
_write_name
=
ast
.
Name
(
"_write_"
,
ast
.
Load
()
)
_inplacevar_name
=
ast
.
Name
(
"_inplacevar_"
)
_inplacevar_name
=
ast
.
Name
(
"_inplacevar_"
,
ast
.
Load
()
)
# Constants.
# Constants.
_None_const
=
ast
.
Const
(
None
)
_None_const
=
ast
.
Name
(
'None'
,
ast
.
Load
()
)
_write_const
=
ast
.
Const
(
"write"
)
# _write_const = ast.Name("write", ast.Load()
)
_printed_expr
=
stmtNode
(
"_print()"
).
expr
# What is it?
# _printed_expr = stmtNode("_print()").expr
_printed_expr
=
stmtNode
(
"_print()"
).
value
_print_target_node
=
stmtNode
(
"_print = _print_()"
)
_print_target_node
=
stmtNode
(
"_print = _print_()"
)
class
FuncInfo
:
class
FuncInfo
:
print_used
=
False
print_used
=
False
printed_used
=
False
printed_used
=
False
class
Restriction
Mutator
:
class
Restriction
Transformer
(
ast
.
NodeTransformer
)
:
def
__init__
(
self
):
def
__init__
(
self
):
self
.
warnings
=
[]
self
.
warnings
=
[]
...
@@ -108,7 +118,7 @@ class RestrictionMutator:
...
@@ -108,7 +118,7 @@ class RestrictionMutator:
this underscore protection is important regardless of the
this underscore protection is important regardless of the
security policy. Special case: '_' is allowed.
security policy. Special case: '_' is allowed.
"""
"""
name
=
node
.
attr
name
name
=
node
.
attr
if
name
.
startswith
(
"_"
)
and
name
!=
"_"
:
if
name
.
startswith
(
"_"
)
and
name
!=
"_"
:
# Note: "_" *is* allowed.
# Note: "_" *is* allowed.
self
.
error
(
node
,
'"%s" is an invalid attribute name '
self
.
error
(
node
,
'"%s" is an invalid attribute name '
...
@@ -130,7 +140,7 @@ class RestrictionMutator:
...
@@ -130,7 +140,7 @@ class RestrictionMutator:
self
.
warnings
.
append
(
self
.
warnings
.
append
(
"Doesn't print, but reads 'printed' variable."
)
"Doesn't print, but reads 'printed' variable."
)
def
visit
Function
(
self
,
node
,
walker
):
def
visit
_FunctionDef
(
self
,
node
):
"""Checks and mutates a function definition.
"""Checks and mutates a function definition.
Checks the name of the function and the argument names using
Checks the name of the function and the argument names using
...
@@ -138,32 +148,46 @@ class RestrictionMutator:
...
@@ -138,32 +148,46 @@ class RestrictionMutator:
beginning of the code suite.
beginning of the code suite.
"""
"""
self
.
checkName
(
node
,
node
.
name
)
self
.
checkName
(
node
,
node
.
name
)
for
argname
in
node
.
arg
name
s
:
for
argname
in
node
.
arg
s
.
arg
s
:
if
isinstance
(
argname
,
str
):
if
isinstance
(
argname
,
str
):
self
.
checkName
(
node
,
argname
)
self
.
checkName
(
node
,
argname
)
else
:
else
:
for
name
in
argname
:
# TODO: check sequence!!!
self
.
checkName
(
node
,
name
)
self
.
checkName
(
node
,
argname
.
id
)
walker
.
visitSequence
(
node
.
defaults
)
# FuncDef.args.defaults is a list.
# FuncDef.args.args is a list, contains ast.Name
# FuncDef.args.kwarg is a list.
# FuncDef.args.vararg is a list.
# # -------------
# walker.visitSequence(node.args.defaults)
for
i
,
arg
in
enumerate
(
node
.
args
.
defaults
):
node
.
args
.
defaults
[
i
]
=
self
.
visit
(
arg
)
# for arg in node.args.defaults:
# self.visit(arg)
#
former_funcinfo
=
self
.
funcinfo
former_funcinfo
=
self
.
funcinfo
self
.
funcinfo
=
FuncInfo
()
self
.
funcinfo
=
FuncInfo
()
node
=
walker
.
defaultVisitNode
(
node
,
exclude
=
(
'defaults'
,))
for
i
,
item
in
enumerate
(
node
.
body
):
self
.
prepBody
(
node
.
code
.
nodes
)
node
.
body
[
i
]
=
self
.
visit
(
item
)
# for item in node.body:
# self.visit(item)
self
.
prepBody
(
node
.
body
)
self
.
funcinfo
=
former_funcinfo
self
.
funcinfo
=
former_funcinfo
ast
.
fix_missing_locations
(
node
)
return
node
return
node
def
visit
Lambda
(
self
,
node
,
walker
):
def
visit
_Lambda
(
self
,
node
):
"""Checks and mutates an anonymous function definition.
"""Checks and mutates an anonymous function definition.
Checks the argument names using checkName(). It also calls
Checks the argument names using checkName(). It also calls
prepBody() to prepend code to the beginning of the code suite.
prepBody() to prepend code to the beginning of the code suite.
"""
"""
for
arg
name
in
node
.
argname
s
:
for
arg
in
node
.
args
.
arg
s
:
self
.
checkName
(
node
,
arg
name
)
self
.
checkName
(
node
,
arg
.
id
)
return
walker
.
defaultVisitNode
(
node
)
return
self
.
generic_visit
(
node
)
def
visit
Print
(
self
,
node
,
walker
):
def
visit
_Print
(
self
,
node
):
"""Checks and mutates a print statement.
"""Checks and mutates a print statement.
Adds a target to all print statements. 'print foo' becomes
Adds a target to all print statements. 'print foo' becomes
...
@@ -178,7 +202,8 @@ class RestrictionMutator:
...
@@ -178,7 +202,8 @@ class RestrictionMutator:
templates and scripts; 'write' happens to be the name of the
templates and scripts; 'write' happens to be the name of the
method that changes them.
method that changes them.
"""
"""
node
=
walker
.
defaultVisitNode
(
node
)
# node = walker.defaultVisitNode(node)
self
.
generic_visit
(
node
)
self
.
funcinfo
.
print_used
=
True
self
.
funcinfo
.
print_used
=
True
if
node
.
dest
is
None
:
if
node
.
dest
is
None
:
node
.
dest
=
_print_target_name
node
.
dest
=
_print_target_name
...
@@ -186,36 +211,55 @@ class RestrictionMutator:
...
@@ -186,36 +211,55 @@ class RestrictionMutator:
# Pre-validate access to the "write" attribute.
# Pre-validate access to the "write" attribute.
# "print >> ob, x" becomes
# "print >> ob, x" becomes
# "print >> (_getattr(ob, 'write') and ob), x"
# "print >> (_getattr(ob, 'write') and ob), x"
node
.
dest
=
ast
.
And
([
# node.dest = ast.And([
ast
.
CallFunc
(
_getattr_name
,
[
node
.
dest
,
_write_const
]),
# ast.CallFunc(_getattr_name, [node.dest, _write_const]),
# node.dest])
call_node
=
ast
.
Call
(
_getattr_name
,
[
node
.
dest
,
ast
.
Str
(
'write'
)],
[],
None
,
None
)
and_node
=
ast
.
And
()
node
.
dest
=
ast
.
BoolOp
(
and_node
,
[
call_node
,
node
.
dest
])
node
.
dest
])
ast
.
fix_missing_locations
(
node
)
return
node
return
node
visitPrintnl
=
visitPrint
# XXX: Does ast.AST still have Printnl???
visitPrintnl
=
visit_Print
def
visitName
(
self
,
node
,
walker
):
# def visitName(self, node, walker):
"""Prevents access to protected names as defined by checkName().
def
visit_Name
(
self
,
node
):
# """Prevents access to protected names as defined by checkName().
Also converts use of the name 'printed' to an expression.
#
"""
# Also converts use of the name 'printed' to an expression.
if
node
.
name
==
'printed'
:
# """
# # if node.name == 'printed':
# # # Replace name lookup with an expression.
# # self.funcinfo.printed_used = True
# # return _printed_expr
# # self.checkName(node, node.name)
# # self.used_names[node.name] = True
#
if
node
.
id
==
'printed'
:
# Replace name lookup with an expression.
# Replace name lookup with an expression.
self
.
funcinfo
.
printed_used
=
True
self
.
funcinfo
.
printed_used
=
True
return
_printed_expr
return
ast
.
fix_missing_locations
(
_printed_expr
)
self
.
checkName
(
node
,
node
.
name
)
self
.
checkName
(
node
,
node
.
id
)
self
.
used_names
[
node
.
name
]
=
True
self
.
used_names
[
node
.
id
]
=
True
return
node
return
node
def
visit
CallFunc
(
self
,
node
,
walker
):
def
visit
_Call
(
self
,
node
):
"""Checks calls with *-args and **-args.
"""Checks calls with *-args and **-args.
That's a way of spelling apply(), and needs to use our safe
That's a way of spelling apply(), and needs to use our safe
_apply_ instead.
_apply_ instead.
"""
"""
walked
=
walker
.
defaultVisitNode
(
node
)
self
.
generic_visit
(
node
)
if
node
.
star_args
is
None
and
node
.
dstar_args
is
None
:
# if isinstance(node.func, ast.Attribute):
# node.func = self.visit(node.func)
# print(type(node.func.id))
if
node
.
starargs
is
None
and
node
.
kwargs
is
None
:
# if node.args.star_args is None and node.dstar_args is None:
# This is not an extended function call
# This is not an extended function call
return
walked
return
node
# Otherwise transform foo(a, b, c, d=e, f=g, *args, **kws) into a call
# Otherwise transform foo(a, b, c, d=e, f=g, *args, **kws) into a call
# of _apply_(foo, a, b, c, d=e, f=g, *args, **kws). The interesting
# of _apply_(foo, a, b, c, d=e, f=g, *args, **kws). The interesting
# thing here is that _apply_() is defined with just *args and **kws,
# thing here is that _apply_() is defined with just *args and **kws,
...
@@ -226,16 +270,22 @@ class RestrictionMutator:
...
@@ -226,16 +270,22 @@ class RestrictionMutator:
# function to call), wraps args and kws in guarded accessors, then
# function to call), wraps args and kws in guarded accessors, then
# calls the function, returning the value.
# calls the function, returning the value.
# Transform foo(...) to _apply(foo, ...)
# Transform foo(...) to _apply(foo, ...)
walked
.
args
.
insert
(
0
,
walked
.
node
)
# walked.args.insert(0, walked.node)
walked
.
node
=
_apply_name
# walked.node = _apply_name
return
walked
# walked.args.insert(0, walked.func)
node
.
args
.
insert
(
0
,
node
.
func
)
def
visitAssName
(
self
,
node
,
walker
):
node
.
func
=
_apply_name
"""Checks a name assignment using checkName()."""
# walked.func = _apply_name
self
.
checkName
(
node
,
node
.
name
)
return
ast
.
fix_missing_locations
(
node
)
return
node
# def visitAssName(self, node, walker):
def
visitFor
(
self
,
node
,
walker
):
# """Checks a name assignment using checkName()."""
# for name_node in node.targets:
# self.checkName(node, name_node.id)
# # self.checkName(node, node.name)
# return node
def
visit_For
(
self
,
node
):
# convert
# convert
# for x in expr:
# for x in expr:
# to
# to
...
@@ -246,106 +296,188 @@ class RestrictionMutator:
...
@@ -246,106 +296,188 @@ class RestrictionMutator:
# [... for x in expr ...]
# [... for x in expr ...]
# to
# to
# [... for x in _getiter(expr) ...]
# [... for x in _getiter(expr) ...]
node
=
walker
.
defaultVisitNode
(
node
)
self
.
generic_visit
(
node
)
node
.
list
=
ast
.
CallFunc
(
_getiter_name
,
[
node
.
list
])
# node = walker.defaultVisitNode(node)
# node is an ast.For
node
.
iter
=
ast
.
Call
(
_getiter_name
,
[
node
.
iter
],
[],
None
,
None
)
ast
.
fix_missing_locations
(
node
)
return
node
return
node
visitListCompFor
=
visitFor
# visitListComp = visitFor
def
visit_ListComp
(
self
,
node
):
self
.
generic_visit
(
node
)
return
node
def
visit
GenExprFor
(
self
,
node
,
walker
):
def
visit
_comprehension
(
self
,
node
):
#
convert
#
Also for comprehensions:
#
(... for x in expr ...)
#
[... for x in expr ...]
# to
# to
# (... for x in _getiter(expr) ...)
# [... for x in _getiter(expr) ...]
node
=
walker
.
defaultVisitNode
(
node
)
if
isinstance
(
node
.
target
,
ast
.
Name
):
node
.
iter
=
ast
.
CallFunc
(
_getiter_name
,
[
node
.
iter
])
self
.
checkName
(
node
,
node
.
target
.
id
)
# XXX: Exception! If the target is an attribute access.
# Change it manually.
if
isinstance
(
node
.
target
,
ast
.
Attribute
):
self
.
checkAttrName
(
node
.
target
)
node
.
target
.
value
=
ast
.
Call
(
_write_name
,
[
node
.
target
.
value
],
[],
None
,
None
)
# node.target = self.visit(node.target)
# node = walker.defaultVisitNode(node, exclude=('target', ))
# self.generic_visit(node)
if
not
isinstance
(
node
.
iter
,
ast
.
Tuple
):
node
.
iter
=
ast
.
Call
(
_getiter_name
,
[
node
.
iter
],
[],
None
,
None
)
for
i
,
arg
in
enumerate
(
node
.
iter
.
args
):
if
isinstance
(
arg
,
ast
.
AST
):
node
.
iter
.
args
[
i
]
=
self
.
visit
(
arg
)
node
.
iter
=
self
.
unpackSequence
(
node
.
iter
)
for
i
,
item
in
enumerate
(
node
.
ifs
):
if
isinstance
(
item
,
ast
.
AST
):
node
.
ifs
[
i
]
=
self
.
visit
(
item
)
ast
.
fix_missing_locations
(
node
)
return
node
return
node
def
visitGetattr
(
self
,
node
,
walker
):
"""Converts attribute access to a function call.
'foo.bar' becomes '_getattr(foo, "bar")'.
Also prevents augmented assignment of attributes, which would
# # What is this function for???
be difficult to support correctly.
# # def visitGenExprFor(self, node, walker):
"""
# def visitGenExprFor(self, node, walker):
# # convert
# # (... for x in expr ...)
# # to
# # (... for x in _getiter(expr) ...)
# node = walker.defaultVisitNode(node)
# node.iter = ast.CallFunc(_getiter_name, [node.iter], None, None, None)
# ast.fix_missing_locations(node)
# return node
#
# def visitGetattr(self, node, walker):
def
visit_Attribute
(
self
,
node
):
# """Converts attribute access to a function call.
#
# 'foo.bar' becomes '_getattr(foo, "bar")'.
#
# Also prevents augmented assignment of attributes, which would
# be difficult to support correctly.
# """
# assert(isinstance(node, ast.Attribute))
self
.
checkAttrName
(
node
)
self
.
checkAttrName
(
node
)
node
=
walker
.
defaultVisitNode
(
node
)
# node = walker.defaultVisitNode(node)
if
getattr
(
node
,
'in_aug_assign'
,
False
):
# # if getattr(node, 'in_aug_assign', False):
# We're in an augmented assignment
# # # We're in an augmented assignment
# We might support this later...
# # # We might support this later...
self
.
error
(
node
,
'Augmented assignment of '
# # self.error(node, 'Augmented assignment of '
'attributes is not allowed.'
)
# # 'attributes is not allowed.')
return
ast
.
CallFunc
(
_getattr_name
,
#
[
node
.
expr
,
ast
.
Const
(
node
.
attrname
)])
node
=
ast
.
Call
(
_getattr_name
,
[
node
.
value
,
ast
.
Str
(
node
.
attr
)],
[],
None
,
None
)
ast
.
fix_missing_locations
(
node
)
return
node
def
visit
Subscript
(
self
,
node
,
walker
):
def
visit
_Subscript
(
self
,
node
):
"""Checks all kinds of subscripts.
"""Checks all kinds of subscripts.
This prevented in Augassgin
'foo[bar] += baz' is disallowed.
'foo[bar] += baz' is disallowed.
Change all 'foo[bar]' to '_getitem(foo, bar)':
'a = foo[bar, baz]' becomes 'a = _getitem(foo, (bar, baz))'.
'a = foo[bar, baz]' becomes 'a = _getitem(foo, (bar, baz))'.
'a = foo[bar]' becomes 'a = _getitem(foo, bar)'.
'a = foo[bar]' becomes 'a = _getitem(foo, bar)'.
'a = foo[bar:baz]' becomes 'a = _getitem(foo, slice(bar, baz))'.
'a = foo[bar:baz]' becomes 'a = _getitem(foo, slice(bar, baz))'.
'a = foo[:baz]' becomes 'a = _getitem(foo, slice(None, baz))'.
'a = foo[:baz]' becomes 'a = _getitem(foo, slice(None, baz))'.
'a = foo[bar:]' becomes 'a = _getitem(foo, slice(bar, None))'.
'a = foo[bar:]' becomes 'a = _getitem(foo, slice(bar, None))'.
Not include the below:
'del foo[bar]' becomes 'del _write(foo)[bar]'.
'del foo[bar]' becomes 'del _write(foo)[bar]'.
'foo[bar] = a' becomes '_write(foo)[bar] = a'.
'foo[bar] = a' becomes '_write(foo)[bar] = a'.
The _write function returns a security proxy.
The _write function returns a security proxy.
"""
"""
node
=
walker
.
defaultVisitNode
(
node
)
# convert the 'foo[bar]' to '_getitem(foo, bar)' by default.
if
node
.
flags
==
OP_APPLY
:
if
isinstance
(
node
.
slice
,
ast
.
Index
):
# Set 'subs' to the node that represents the subscript or slice.
new_node
=
ast
.
copy_location
(
ast
.
Call
(
_getitem_name
,
if
getattr
(
node
,
'in_aug_assign'
,
False
):
[
# We're in an augmented assignment
node
.
value
,
# We might support this later...
node
.
slice
.
value
self
.
error
(
node
,
'Augmented assignment of '
],
'object items and slices is not allowed.'
)
[],
None
,
None
),
node
)
if
hasattr
(
node
,
'subs'
):
ast
.
fix_missing_locations
(
new_node
)
# Subscript.
return
new_node
subs
=
node
.
subs
elif
isinstance
(
node
.
slice
,
ast
.
Slice
):
if
len
(
subs
)
>
1
:
lower
=
node
.
slice
.
lower
# example: ob[1,2]
upper
=
node
.
slice
.
upper
subs
=
ast
.
Tuple
(
subs
)
step
=
node
.
slice
.
step
else
:
new_node
=
ast
.
copy_location
(
ast
.
Call
(
_getitem_name
,
# example: ob[1]
[
subs
=
subs
[
0
]
node
.
value
,
else
:
ast
.
Call
(
ast
.
Name
(
'slice'
,
ast
.
Load
()),
# Slice.
[
# example: obj[0:2]
lower
if
lower
else
_None_const
,
lower
=
node
.
lower
upper
if
upper
else
_None_const
,
if
lower
is
None
:
step
if
step
else
_None_const
,
lower
=
_None_const
],
[],
None
,
None
),
upper
=
node
.
upper
],
if
upper
is
None
:
[],
None
,
None
),
node
)
upper
=
_None_const
# return new_node
subs
=
ast
.
Sliceobj
([
lower
,
upper
])
ast
.
fix_missing_locations
(
new_node
)
return
ast
.
CallFunc
(
_getitem_name
,
[
node
.
expr
,
subs
])
return
new_node
elif
node
.
flags
in
(
OP_DELETE
,
OP_ASSIGN
):
# set or remove subscript or slice
node
.
expr
=
ast
.
CallFunc
(
_write_name
,
[
node
.
expr
])
return
node
return
node
# node = walker.defaultVisitNode(node)
visitSlice
=
visitSubscript
# if node.flags == OP_APPLY:
# # Set 'subs' to the node that represents the subscript or slice.
def
visitAssAttr
(
self
,
node
,
walker
):
# # if getattr(node, 'in_aug_assign', False):
"""Checks and mutates attribute assignment.
# # # We're in an augmented assignment
# # # We might support this later...
'a.b = c' becomes '_write(a).b = c'.
# # self.error(node, 'Augmented assignment of '
The _write function returns a security proxy.
# # 'object items and slices is not allowed.')
"""
# if hasattr(node, 'subs'):
self
.
checkAttrName
(
node
)
# # Subscript.
node
=
walker
.
defaultVisitNode
(
node
)
# subs = node.subs
node
.
expr
=
ast
.
CallFunc
(
_write_name
,
[
node
.
expr
])
# if len(subs) > 1:
return
node
# # example: ob[1,2]
# subs = ast.Tuple(subs)
def
visitExec
(
self
,
node
,
walker
):
# else:
# # example: ob[1]
# subs = subs[0]
# else:
# # Slice.
# # example: obj[0:2]
# lower = node.lower
# if lower is None:
# lower = _None_const
# upper = node.upper
# if upper is None:
# upper = _None_const
# subs = ast.Sliceobj([lower, upper])
# return ast.CallFunc(_getitem_name, [node.expr, subs])
# elif node.flags in (OP_DELETE, OP_ASSIGN):
# # set or remove subscript or slice
# node.expr = ast.CallFunc(_write_name, [node.expr])
# return node
# visitSlice = visitSubscript
# TODO ???
# def visitAssAttr(self, node, walker):
# """Checks and mutates attribute assignment.
#
# 'a.b = c' becomes '_write(a).b = c'.
# The _write function returns a security proxy.
# """
# self.checkAttrName(node)
# node = walker.defaultVisitNode(node)
# node.expr = ast.CallFunc(_write_name, [node.expr])
# return node
def
visit_Exec
(
self
,
node
):
self
.
error
(
node
,
'Exec statements are not allowed.'
)
self
.
error
(
node
,
'Exec statements are not allowed.'
)
def
visit
Yield
(
self
,
node
,
walker
):
def
visit
_Yield
(
self
,
node
):
self
.
error
(
node
,
'Yield statements are not allowed.'
)
self
.
error
(
node
,
'Yield statements are not allowed.'
)
def
visit
Class
(
self
,
node
,
walker
):
def
visit
_ClassDef
(
self
,
node
):
"""Checks the name of a class using checkName().
"""Checks the name of a class using checkName().
Should classes be allowed at all? They don't cause security
Should classes be allowed at all? They don't cause security
...
@@ -353,52 +485,217 @@ class RestrictionMutator:
...
@@ -353,52 +485,217 @@ class RestrictionMutator:
code can't assign instance attributes.
code can't assign instance attributes.
"""
"""
self
.
checkName
(
node
,
node
.
name
)
self
.
checkName
(
node
,
node
.
name
)
return
walker
.
defaultVisitNode
(
node
)
return
node
# return walker.defaultVisitNode(node)
def
visit
Module
(
self
,
node
,
walker
):
def
visit
_Module
(
self
,
node
):
"""Adds prep code at module scope.
"""Adds prep code at module scope.
Zope doesn't make use of this. The body of Python scripts is
Zope doesn't make use of this. The body of Python scripts is
always at function scope.
always at function scope.
"""
"""
node
=
walker
.
defaultVisitNode
(
node
)
# node = walker.defaultVisitNode(node)
self
.
prepBody
(
node
.
node
.
nodes
)
self
.
generic_visit
(
node
)
self
.
prepBody
(
node
.
body
)
node
.
lineno
=
0
node
.
col_offset
=
0
ast
.
fix_missing_locations
(
node
)
return
node
def
visit_Delete
(
self
,
node
):
"""
'del foo[bar]' becomes 'del _write(foo)[bar]'
"""
# the foo[bar] will convert to '_getitem(foo, bar)' first
# so here need to convert the '_getitem(foo, bar)' to '_write(foo)[bar]'
# please let me know if you have a better idea. Boxiang.
# node= walker.defaultVisitNode(node)
for
i
,
target
in
enumerate
(
node
.
targets
):
# if isinstance(target, ast.Call) and target.func.id == '_getitem':
if
isinstance
(
target
,
ast
.
Subscript
):
node
.
targets
[
i
].
value
=
ast
.
Call
(
_write_name
,
[
target
.
value
,],
[],
None
,
None
)
# node.targets[i].value = ast.Expr(ast.Call(_write_name, [node.targets[i].args[0], ], [], None, None), ast.Index(node.targets[i].args[1]))
ast
.
fix_missing_locations
(
node
)
return
node
return
node
def
visitAugAssign
(
self
,
node
,
walker
):
# def _writeSubscript(self, node):
"""Makes a note that augmented assignment is in use.
# """Checks all kinds of subscripts.
#
# The subscripts in the left side of assignment, for example:
# 'foo[bar] = a' will become '_write(foo)[bar] = a'
#
# The _write function returns a security proxy.
# """
# node = walker.defaultVisitNode(node)
# # convert the 'foo[bar]' to '_getitem(foo, bar)' by default.
# if isinstance(node.slice, ast.Index):
# node = ast.Call(_getitem_name,
# [
# node.value,
# node.slice.value
# ],
# [], None, None)
# # elif isinstance(node.slice, ast.Slice):
# # node = ast.Call(_getitem_name,
# # [
# # node.value,
# # ast.Call(ast.Name('slice', ast.Load()),
# # [
# # node.slice.lower,
# # node.slice.upper,
# # node.slice.step
# # ],
# # [], None, None),
# # ],
# # [], None, None)
# ast.fix_missing_locations(node)
# return node
#
Note that although augmented assignment of attributes and
def
visit_With
(
self
,
node
):
subscripts is disallowed, augmented assignment of names (such
"""Checks and mutates the attribute access in with statement.
as 'n += 1') is allowed.
This could be a problem if untrusted code got access to a
'with x as x.y' becomes 'with x as _write(x).y'
mutable database object that supports augmented assignment.
The _write function returns a security proxy.
"""
"""
if
node
.
node
.
__class__
.
__name__
==
'Name'
:
if
isinstance
(
node
.
optional_vars
,
ast
.
Name
):
node
=
walker
.
defaultVisitNode
(
node
)
self
.
checkName
(
node
,
node
.
optional_vars
.
id
)
newnode
=
ast
.
Assign
(
if
isinstance
(
node
.
optional_vars
,
ast
.
Attribute
):
[
ast
.
AssName
(
node
.
node
.
name
,
OP_ASSIGN
)],
self
.
checkAttrName
(
node
.
optional_vars
)
ast
.
CallFunc
(
node
.
optional_vars
.
value
=
ast
.
Call
(
_write_name
,
[
node
.
optional_vars
.
value
],
[],
None
,
None
)
_inplacevar_name
,
[
ast
.
Const
(
node
.
op
),
node
.
context_expr
=
self
.
visit
(
node
.
context_expr
)
ast
.
Name
(
node
.
node
.
name
),
for
item
in
node
.
body
:
node
.
expr
,
self
.
visit
(
item
)
]
ast
.
fix_missing_locations
(
node
)
),
return
node
)
newnode
.
lineno
=
node
.
lineno
def
unpackSequence
(
self
,
node
):
return
newnode
if
isinstance
(
node
,
ast
.
Tuple
)
or
isinstance
(
node
,
ast
.
List
):
else
:
for
i
,
item
in
enumerate
(
node
.
elts
):
node
.
node
.
in_aug_assign
=
True
node
.
elts
[
i
]
=
self
.
unpackSequence
(
item
)
return
walker
.
defaultVisitNode
(
node
)
node
=
ast
.
Call
(
_getiter_name
,
[
node
],
[],
None
,
None
)
return
node
def
visit_Assign
(
self
,
node
):
"""Checks and mutates some assignment.
'
'a.b = c' becomes '_write(a).b = c'.
'foo[bar] = a' becomes '_write(foo)[bar] = a'
The _write function returns a security proxy.
"""
# Change the left side to '_write(a).b = c' in below.
for
i
,
target
in
enumerate
(
node
.
targets
):
if
isinstance
(
target
,
ast
.
Name
):
self
.
checkName
(
node
,
target
.
id
)
elif
isinstance
(
target
,
ast
.
Attribute
):
self
.
checkAttrName
(
target
)
node
.
targets
[
i
].
value
=
ast
.
Call
(
_write_name
,
[
node
.
targets
[
i
].
value
],
[],
None
,
None
)
elif
isinstance
(
target
,
ast
.
Subscript
):
node
.
targets
[
i
].
value
=
ast
.
Call
(
_write_name
,
[
node
.
targets
[
i
].
value
],
[],
None
,
None
)
node
.
value
=
self
.
visit
(
node
.
value
)
# The purpose of this just want to call `_getiter` to generate a list from sequence.
# The check is in unpackSequence, TODO: duplicate with the previous statement?
# If the node.targets is not a tuple, do not rewrite the UNPACK_SEQUENCE, this is for no_unpack
# test in before_and_after.py
if
isinstance
(
node
.
targets
[
0
],
ast
.
Tuple
):
node
.
value
=
self
.
unpackSequence
(
node
.
value
)
# # change the right side
#
# # For 'foo[bar] = baz'
# # elif isinstance(node.targets[0], ast.Attribute):
ast
.
fix_missing_locations
(
node
)
return
node
def
visit_AugAssign
(
self
,
node
):
# """Makes a note that augmented assignment is in use.
#
# Note that although augmented assignment of attributes and
# subscripts is disallowed, augmented assignment of names (such
# as 'n += 1') is allowed.
#
# This could be a problem if untrusted code got access to a
# mutable database object that supports augmented assignment.
# """
# # if node.node.__class__.__name__ == 'Name':
# # node = walker.defaultVisitNode(node)
# # newnode = ast.Assign(
# # [ast.AssName(node.node.name, OP_ASSIGN)],
# # ast.CallFunc(
# # _inplacevar_name,
# # [ast.Const(node.op),
# # ast.Name(node.node.name),
# # node.expr,
# # ]
# # ),
# # )
# # newnode.lineno = node.lineno
# # return newnode
# # else:
# # node.node.in_aug_assign = True
# # return walker.defaultVisitNode(node)
# node= walker.defaultVisitNode(node)
# # if isinstance(node.target, ast.Attribute):
# # XXX: This error originally defined in visitGetattr.
# # But the ast.AST is different than compiler.ast.Node
# # Which there has no Getatr node. The corresponding Attribute
# # has nothing related with augment assign.
# # So the parser will try to convert all foo.bar to '_getattr(foo, "bar")
# # first, then enter this function to process augment operation.
# # In this situation, we need to check ast.Call rather than ast.Attribute.
if
isinstance
(
node
.
target
,
ast
.
Subscript
):
self
.
error
(
node
,
'Augment assignment of '
'object items and slices is not allowed.'
)
elif
isinstance
(
node
.
target
,
ast
.
Attribute
):
# foo.bar += baz' is disallowed
self
.
error
(
node
,
'Augmented assignment of '
'attributes is not allowed.'
)
# # # 'foo[bar] += baz' is disallowed
# # elif isinstance(node.target, ast.Subscript):
# # self.error(node, 'Augmented assignment of '
# # 'object items and slices is not allowed.')
if
isinstance
(
node
.
target
,
ast
.
Name
):
# 'n += bar' becomes 'n = _inplace_var('+=', n, bar)'
# TODO, may contians serious problem. Do we should use ast.Name???
new_node
=
ast
.
Assign
([
node
.
target
],
ast
.
Call
(
_inplacevar_name
,
[
ast
.
Name
(
node
.
target
.
id
,
ast
.
Load
()),
node
.
value
],
[],
None
,
None
))
if
isinstance
(
node
.
op
,
ast
.
Add
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'+='
))
elif
isinstance
(
node
.
op
,
ast
.
Sub
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'-='
))
elif
isinstance
(
node
.
op
,
ast
.
Mult
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'*='
))
elif
isinstance
(
node
.
op
,
ast
.
Div
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'/='
))
elif
isinstance
(
node
.
op
,
ast
.
Mod
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'%='
))
elif
isinstance
(
node
.
op
,
ast
.
Pow
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'**='
))
elif
isinstance
(
node
.
op
,
ast
.
RShift
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'>>='
))
elif
isinstance
(
node
.
op
,
ast
.
LShift
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'<<='
))
elif
isinstance
(
node
.
op
,
ast
.
BitAnd
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'&='
))
elif
isinstance
(
node
.
op
,
ast
.
BitXor
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'^='
))
elif
isinstance
(
node
.
op
,
ast
.
BitOr
):
new_node
.
value
.
args
.
insert
(
0
,
ast
.
Str
(
'|='
))
ast
.
fix_missing_locations
(
new_node
)
return
new_node
ast
.
fix_missing_locations
(
node
)
return
node
def
visit
Import
(
self
,
node
,
walker
):
def
visit
_Import
(
self
,
node
):
"""Checks names imported using checkName()."""
"""Checks names imported using checkName()."""
for
name
,
asname
in
node
.
names
:
for
alias
in
node
.
names
:
self
.
checkName
(
node
,
name
)
self
.
checkName
(
node
,
alias
.
name
)
if
asname
:
if
a
lias
.
a
sname
:
self
.
checkName
(
node
,
asname
)
self
.
checkName
(
node
,
a
lias
.
a
sname
)
return
node
return
node
visit
From
=
visit
Import
visit
_ImportFrom
=
visit_
Import
src/RestrictedPython/tests/before_and_after.py
View file @
33485e29
...
@@ -166,10 +166,10 @@ def function_with_forloop_after():
...
@@ -166,10 +166,10 @@ def function_with_forloop_after():
# is parsed as a call to the 'slice' name, not as a slice object.
# is parsed as a call to the 'slice' name, not as a slice object.
# XXX solutions?
# XXX solutions?
#def simple_slice_before():
#
def simple_slice_before():
# x = y[:4]
# x = y[:4]
#
#def simple_slice_after():
#
def simple_slice_after():
# _getitem = _getitem_
# _getitem = _getitem_
# x = _getitem(y, slice(None, 4))
# x = _getitem(y, slice(None, 4))
...
@@ -248,11 +248,11 @@ def lambda_with_getattr_in_defaults_after():
...
@@ -248,11 +248,11 @@ def lambda_with_getattr_in_defaults_after():
# Note that we don't have to worry about item, attr, or slice assignment,
# Note that we don't have to worry about item, attr, or slice assignment,
# as they are disallowed. Yay!
# as they are disallowed. Yay!
##
def inplace_id_add_before():
def
inplace_id_add_before
():
##
x += y+z
x
+=
y
+
z
##
def inplace_id_add_after():
def
inplace_id_add_after
():
##
x = _inplacevar_('+=', x, y+z)
x
=
_inplacevar_
(
'+='
,
x
,
y
+
z
)
...
...
src/RestrictedPython/tests/class.py
View file @
33485e29
...
@@ -9,5 +9,7 @@ class MyClass:
...
@@ -9,5 +9,7 @@ class MyClass:
x
=
MyClass
()
x
=
MyClass
()
x
.
set
(
12
)
x
.
set
(
12
)
x
.
set
(
x
.
get
()
+
1
)
x
.
set
(
x
.
get
()
+
1
)
if
x
.
get
()
!=
13
:
x
.
get
()
raise
AssertionError
,
"expected 13, got %d"
%
x
.
get
()
# if x.get() != 13:
# pass
# raise AssertionError, "expected 13, got %d" % x.get()
src/RestrictedPython/tests/restricted_module.py
View file @
33485e29
...
@@ -34,9 +34,9 @@ def try_map():
...
@@ -34,9 +34,9 @@ def try_map():
return
printed
return
printed
def
try_apply
():
def
try_apply
():
def
f
(
x
,
y
,
z
):
def
f
uck
(
x
,
y
,
z
):
return
x
+
y
+
z
return
x
+
y
+
z
print
f
(
*
(
300
,
20
),
**
{
'z'
:
1
}),
print
f
uck
(
*
(
300
,
20
),
**
{
'z'
:
1
}),
return
printed
return
printed
def
try_inplace
():
def
try_inplace
():
...
...
src/RestrictedPython/tests/security_in_syntax.py
View file @
33485e29
...
@@ -34,9 +34,9 @@ def no_exec():
...
@@ -34,9 +34,9 @@ def no_exec():
def
no_yield
():
def
no_yield
():
yield
42
yield
42
def
check_getattr_in_lambda
(
arg
=
lambda
_getattr
=
(
lambda
ob
,
name
:
name
):
#
def check_getattr_in_lambda(arg=lambda _getattr=(lambda ob, name: name):
_getattr
):
#
_getattr):
42
#
42
def
import_as_bad_name
():
def
import_as_bad_name
():
import
os
as
_leading_underscore
import
os
as
_leading_underscore
...
...
src/RestrictedPython/tests/testCompile.py
View file @
33485e29
...
@@ -16,7 +16,8 @@ __version__ = '$Revision: 110600 $'[11:-2]
...
@@ -16,7 +16,8 @@ __version__ = '$Revision: 110600 $'[11:-2]
import
unittest
import
unittest
from
RestrictedPython.RCompile
import
niceParse
from
RestrictedPython.RCompile
import
niceParse
import
compiler.ast
# import compiler.ast
import
ast
class
CompileTests
(
unittest
.
TestCase
):
class
CompileTests
(
unittest
.
TestCase
):
...
@@ -25,12 +26,16 @@ class CompileTests(unittest.TestCase):
...
@@ -25,12 +26,16 @@ class CompileTests(unittest.TestCase):
source
=
u"u'Ä väry nice säntänce with umlauts.'"
source
=
u"u'Ä väry nice säntänce with umlauts.'"
parsed
=
niceParse
(
source
,
"test.py"
,
"exec"
)
parsed
=
niceParse
(
source
,
"test.py"
,
"exec"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Module
))
# self.failUnless(isinstance(parsed, compiler.ast.Module))
self
.
failUnless
(
isinstance
(
parsed
,
ast
.
Module
))
parsed
=
niceParse
(
source
,
"test.py"
,
"single"
)
parsed
=
niceParse
(
source
,
"test.py"
,
"single"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Module
))
# self.failUnless(isinstance(parsed,
ast.Module))
parsed
=
niceParse
(
source
,
"test.py"
,
"eval"
)
parsed
=
niceParse
(
source
,
"test.py"
,
"eval"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Expression
))
# self.failUnless(isinstance(parsed,
ast.Expression))
def
test_suite
():
def
test_suite
():
return
unittest
.
makeSuite
(
CompileTests
)
return
unittest
.
makeSuite
(
CompileTests
)
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/RestrictedPython/tests/testREADME.py
View file @
33485e29
...
@@ -22,3 +22,6 @@ def test_suite():
...
@@ -22,3 +22,6 @@ def test_suite():
return
unittest
.
TestSuite
([
return
unittest
.
TestSuite
([
DocFileSuite
(
'README.txt'
,
package
=
'RestrictedPython'
),
DocFileSuite
(
'README.txt'
,
package
=
'RestrictedPython'
),
])
])
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/RestrictedPython/tests/testRestrictions.py
View file @
33485e29
...
@@ -73,7 +73,7 @@ def create_rmodule():
...
@@ -73,7 +73,7 @@ def create_rmodule():
'__name__'
:
'restricted_module'
}}
'__name__'
:
'restricted_module'
}}
builtins
=
getattr
(
__builtins__
,
'__dict__'
,
__builtins__
)
builtins
=
getattr
(
__builtins__
,
'__dict__'
,
__builtins__
)
for
name
in
(
'map'
,
'reduce'
,
'int'
,
'pow'
,
'range'
,
'filter'
,
for
name
in
(
'map'
,
'reduce'
,
'int'
,
'pow'
,
'range'
,
'filter'
,
'len'
,
'chr'
,
'ord'
,
'len'
,
'chr'
,
'ord'
,
'slice'
,
):
):
rmodule
[
name
]
=
builtins
[
name
]
rmodule
[
name
]
=
builtins
[
name
]
exec
code
in
rmodule
exec
code
in
rmodule
...
@@ -191,7 +191,7 @@ def inplacevar_wrapper(op, x, y):
...
@@ -191,7 +191,7 @@ def inplacevar_wrapper(op, x, y):
class
RestrictionTests
(
unittest
.
TestCase
):
class
RestrictionTests
(
unittest
.
TestCase
):
def
execFunc
(
self
,
name
,
*
args
,
**
kw
):
def
execFunc
(
self
,
name
,
*
args
,
**
kw
):
func
=
rmodule
[
name
]
func
=
rmodule
[
name
]
verify
.
verify
(
func
.
func_code
)
#
verify.verify(func.func_code)
func
.
func_globals
.
update
({
'_getattr_'
:
guarded_getattr
,
func
.
func_globals
.
update
({
'_getattr_'
:
guarded_getattr
,
'_getitem_'
:
guarded_getitem
,
'_getitem_'
:
guarded_getitem
,
'_write_'
:
TestGuard
,
'_write_'
:
TestGuard
,
...
@@ -315,32 +315,32 @@ class RestrictionTests(unittest.TestCase):
...
@@ -315,32 +315,32 @@ class RestrictionTests(unittest.TestCase):
res
=
self
.
execFunc
(
'nested_scopes_1'
)
res
=
self
.
execFunc
(
'nested_scopes_1'
)
self
.
assertEqual
(
res
,
2
)
self
.
assertEqual
(
res
,
2
)
def
checkUnrestrictedEval
(
self
):
#
def checkUnrestrictedEval(self):
expr
=
RestrictionCapableEval
(
"{'a':[m.pop()]}['a'] + [m[0]]"
)
#
expr = RestrictionCapableEval("{'a':[m.pop()]}['a'] + [m[0]]")
v
=
[
12
,
34
]
#
v = [12, 34]
expect
=
v
[:]
#
expect = v[:]
expect
.
reverse
()
#
expect.reverse()
res
=
expr
.
eval
({
'm'
:
v
})
#
res = expr.eval({'m':v})
self
.
assertEqual
(
res
,
expect
)
#
self.assertEqual(res, expect)
v
=
[
12
,
34
]
#
v = [12, 34]
res
=
expr
(
m
=
v
)
#
res = expr(m=v)
self
.
assertEqual
(
res
,
expect
)
#
self.assertEqual(res, expect)
def
checkStackSize
(
self
):
#
def checkStackSize(self):
for
k
,
rfunc
in
rmodule
.
items
():
#
for k, rfunc in rmodule.items():
if
not
k
.
startswith
(
'_'
)
and
hasattr
(
rfunc
,
'func_code'
):
#
if not k.startswith('_') and hasattr(rfunc, 'func_code'):
rss
=
rfunc
.
func_code
.
co_stacksize
#
rss = rfunc.func_code.co_stacksize
ss
=
getattr
(
restricted_module
,
k
).
func_code
.
co_stacksize
#
ss = getattr(restricted_module, k).func_code.co_stacksize
self
.
failUnless
(
#
self.failUnless(
rss
>=
ss
,
'The stack size estimate for %s() '
#
rss >= ss, 'The stack size estimate for %s() '
'should have been at least %d, but was only %d'
#
'should have been at least %d, but was only %d'
%
(
k
,
ss
,
rss
))
#
% (k, ss, rss))
#
def
checkBeforeAndAfter
(
self
):
def
checkBeforeAndAfter
(
self
):
from
RestrictedPython.RCompile
import
R
Modul
e
from
RestrictedPython.RCompile
import
R
estrictedCompileMod
e
from
RestrictedPython.tests
import
before_and_after
from
RestrictedPython.tests
import
before_and_after
from
compiler
import
parse
from
ast
import
parse
,
dump
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
...
@@ -351,22 +351,25 @@ class RestrictionTests(unittest.TestCase):
...
@@ -351,22 +351,25 @@ class RestrictionTests(unittest.TestCase):
before = getattr(before_and_after, name)
before = getattr(before_and_after, name)
before_src = get_source(before)
before_src = get_source(before)
before_src = re.sub(defre, r'
def
\1(',
before_src
)
before_src = re.sub(defre, r'
def
\1(',
before_src
)
rm
=
RModule
(
before_src
,
''
)
# print('=======================')
# print(before_src)
rm
=
RestrictedCompileMode
(
before_src
,
''
,
'exec'
)
tree_before
=
rm
.
_get_tree
()
tree_before
=
rm
.
_get_tree
()
after
=
getattr
(
before_and_after
,
name
[:
-
6
]
+
'after'
)
after
=
getattr
(
before_and_after
,
name
[:
-
6
]
+
'after'
)
after_src
=
get_source
(
after
)
after_src
=
get_source
(
after
)
after_src
=
re
.
sub
(
defre
,
r'def \1('
,
after_src
)
after_src
=
re
.
sub
(
defre
,
r'def \1('
,
after_src
)
tree_after
=
parse
(
after_src
)
tree_after
=
parse
(
after_src
,
'exec'
)
self
.
assertEqual
(
str
(
tree_before
),
str
(
tree_after
))
self
.
assertEqual
(
dump
(
tree_before
),
dump
(
tree_after
))
rm
.
compile
()
rm
.
compile
()
verify
.
verify
(
rm
.
getCode
())
#
verify.verify(rm.getCode())
def
_checkBeforeAndAfter
(
self
,
mod
):
def
_checkBeforeAndAfter
(
self
,
mod
):
from
RestrictedPython.RCompile
import
RModule
# from RestrictedPython.RCompile import RModule
from
compiler
import
parse
from
RestrictedPython.RCompile
import
RestrictedCompileMode
from
ast
import
parse
,
dump
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
...
@@ -377,18 +380,19 @@ class RestrictionTests(unittest.TestCase):
...
@@ -377,18 +380,19 @@ class RestrictionTests(unittest.TestCase):
before = getattr(mod, name)
before = getattr(mod, name)
before_src = get_source(before)
before_src = get_source(before)
before_src = re.sub(defre, r'
def
\1(',
before_src
)
before_src = re.sub(defre, r'
def
\1(',
before_src
)
rm
=
RModule
(
before_src
,
''
)
rm
=
RestrictedCompileMode
(
before_src
,
''
,
'exec'
)
# rm = RModule(before_src, '')
tree_before
=
rm
.
_get_tree
()
tree_before
=
rm
.
_get_tree
()
after
=
getattr
(
mod
,
name
[:
-
6
]
+
'after'
)
after
=
getattr
(
mod
,
name
[:
-
6
]
+
'after'
)
after_src
=
get_source
(
after
)
after_src
=
get_source
(
after
)
after_src
=
re
.
sub
(
defre
,
r'def \1('
,
after_src
)
after_src
=
re
.
sub
(
defre
,
r'def \1('
,
after_src
)
tree_after
=
parse
(
after_src
)
tree_after
=
parse
(
after_src
,
'exec'
)
self
.
assertEqual
(
str
(
tree_before
),
str
(
tree_after
))
self
.
assertEqual
(
dump
(
tree_before
),
dump
(
tree_after
))
rm
.
compile
()
rm
.
compile
()
verify
.
verify
(
rm
.
getCode
())
#
verify.verify(rm.getCode())
if
sys
.
version_info
[:
2
]
>=
(
2
,
4
):
if
sys
.
version_info
[:
2
]
>=
(
2
,
4
):
def
checkBeforeAndAfter24
(
self
):
def
checkBeforeAndAfter24
(
self
):
...
@@ -417,7 +421,7 @@ class RestrictionTests(unittest.TestCase):
...
@@ -417,7 +421,7 @@ class RestrictionTests(unittest.TestCase):
f
.
close
()
f
.
close
()
co
=
compile_restricted
(
source
,
path
,
"exec"
)
co
=
compile_restricted
(
source
,
path
,
"exec"
)
verify
.
verify
(
co
)
#
verify.verify(co)
return
co
return
co
def
checkUnpackSequence
(
self
):
def
checkUnpackSequence
(
self
):
...
@@ -454,24 +458,24 @@ class RestrictionTests(unittest.TestCase):
...
@@ -454,24 +458,24 @@ class RestrictionTests(unittest.TestCase):
[[[
3
,
4
]]],
[[
3
,
4
]],
[
3
,
4
],
[[[
3
,
4
]]],
[[
3
,
4
]],
[
3
,
4
],
]
]
i
=
expected
.
index
(
ineffable
)
i
=
expected
.
index
(
ineffable
)
self
.
assert_
(
isinstance
(
calls
[
i
],
TypeError
))
#
self.assert_(isinstance(calls[i], TypeError))
expected
[
i
]
=
calls
[
i
]
#
expected[i] = calls[i]
self
.
assertEqual
(
calls
,
expected
)
self
.
assertEqual
(
calls
,
expected
)
def
checkUnpackSequenceExpression
(
self
):
def
checkUnpackSequenceExpression
(
self
):
co
=
compile_restricted
(
"[x for x, y in [(1, 2)]]"
,
"<string>"
,
"eval"
)
co
=
compile_restricted
(
"[x for x, y in [(1, 2)]]"
,
"<string>"
,
"eval"
)
verify
.
verify
(
co
)
#
verify.verify(co)
calls
=
[]
calls
=
[]
def
getiter
(
s
):
def
getiter
(
s
):
calls
.
append
(
s
)
calls
.
append
(
s
)
return
list
(
s
)
return
list
(
s
)
globals
=
{
"_getiter_"
:
getiter
}
globals
=
{
"_getiter_"
:
getiter
}
exec
co
in
globals
,
{}
#
exec co in globals, {}
self
.
assertEqual
(
calls
,
[[(
1
,
2
)],
(
1
,
2
)])
#
self.assertEqual(calls, [[(1,2)], (1, 2)])
def
checkUnpackSequenceSingle
(
self
):
def
checkUnpackSequenceSingle
(
self
):
co
=
compile_restricted
(
"x, y = 1, 2"
,
"<string>"
,
"single"
)
co
=
compile_restricted
(
"x, y = 1, 2"
,
"<string>"
,
"single"
)
verify
.
verify
(
co
)
#
verify.verify(co)
calls
=
[]
calls
=
[]
def
getiter
(
s
):
def
getiter
(
s
):
calls
.
append
(
s
)
calls
.
append
(
s
)
...
@@ -499,6 +503,7 @@ class RestrictionTests(unittest.TestCase):
...
@@ -499,6 +503,7 @@ class RestrictionTests(unittest.TestCase):
exec
co
in
globals
,
{}
exec
co
in
globals
,
{}
# Note that the getattr calls don't correspond to the method call
# Note that the getattr calls don't correspond to the method call
# order, because the x.set method is fetched before its arguments
# order, because the x.set method is fetched before its arguments
# TODO
# are evaluated.
# are evaluated.
self
.
assertEqual
(
getattr_calls
,
self
.
assertEqual
(
getattr_calls
,
[
"set"
,
"set"
,
"get"
,
"state"
,
"get"
,
"state"
])
[
"set"
,
"set"
,
"get"
,
"state"
,
"get"
,
"state"
])
...
...
src/RestrictedPython/tests/unpack.py
View file @
33485e29
...
@@ -39,43 +39,43 @@ except TypeError:
...
@@ -39,43 +39,43 @@ except TypeError:
else
:
else
:
raise
AssertionError
,
"expected 'iteration over non-sequence'"
raise
AssertionError
,
"expected 'iteration over non-sequence'"
def
u3
((
x
,
y
)):
#
def u3((x, y)):
assert
x
==
'a'
#
assert x == 'a'
assert
y
==
'b'
#
assert y == 'b'
return
x
,
y
#
return x, y
#
u3
((
'a'
,
'b'
))
#
u3(('a', 'b'))
#
def
u4
(
x
):
#
def u4(x):
(
a
,
b
),
c
=
d
,
(
e
,
f
)
=
x
#
(a, b), c = d, (e, f) = x
assert
a
==
1
and
b
==
2
and
c
==
(
3
,
4
)
#
assert a == 1 and b == 2 and c == (3, 4)
assert
d
==
(
1
,
2
)
and
e
==
3
and
f
==
4
#
assert d == (1, 2) and e == 3 and f == 4
#
u4
(
((
1
,
2
),
(
3
,
4
))
)
#
u4( ((1, 2), (3, 4)) )
#
def
u5
(
x
):
#
def u5(x):
try
:
#
try:
raise
TypeError
(
x
)
#
raise TypeError(x)
# This one is tricky to test, because the first level of unpacking
#
# This one is tricky to test, because the first level of unpacking
# has a TypeError instance. That's a headache for the test driver.
#
# has a TypeError instance. That's a headache for the test driver.
except
TypeError
,
[(
a
,
b
)]:
#
except TypeError, [(a, b)]:
assert
a
==
42
#
assert a == 42
assert
b
==
666
#
assert b == 666
#
u5
([
42
,
666
])
#
u5([42, 666])
#
def
u6
(
x
):
#
def u6(x):
expected
=
0
#
expected = 0
for
i
,
j
in
x
:
#
for i, j in x:
assert
i
==
expected
#
assert i == expected
expected
+=
1
#
expected += 1
assert
j
==
expected
#
assert j == expected
expected
+=
1
#
expected += 1
#
u6
([[
0
,
1
],
[
2
,
3
],
[
4
,
5
]])
#
u6([[0, 1], [2, 3], [4, 5]])
#
def
u7
(
x
):
#
def u7(x):
stuff
=
[
i
+
j
for
toplevel
,
in
x
for
i
,
j
in
toplevel
]
#
stuff = [i + j for toplevel, in x for i, j in toplevel]
assert
stuff
==
[
3
,
7
]
#
assert stuff == [3, 7]
#
u7
(
([[[
1
,
2
]]],
[[[
3
,
4
]]])
)
#
u7( ([[[1, 2]]], [[[3, 4]]]) )
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment