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:
if
PROFILE
:
from
time
import
clock
start
=
clock
()
# Pyston change: do nothing
# co, err, warn, used = compile_restricted_eval(
# self.expr, '<string>')
# if PROFILE:
# end = clock()
# print 'prepRestrictedCode: %d ms for %s' % (
# (end - start) * 1000, `self.expr`)
# if err:
# raise SyntaxError, err[0]
# self.used = tuple(used.keys())
# self.rcode = co
co
,
err
,
warn
,
used
=
compile_restricted_eval
(
self
.
expr
,
'<string>'
)
if
PROFILE
:
end
=
clock
()
print
'prepRestrictedCode: %d ms for %s'
%
(
(
end
-
start
)
*
1000
,
`self.expr`
)
if
err
:
raise
SyntaxError
,
err
[
0
]
self
.
used
=
tuple
(
used
.
keys
())
self
.
rcode
=
co
def
prepUnrestrictedCode
(
self
):
if
self
.
ucode
is
None
:
...
...
src/RestrictedPython/MutatingWalker.py
View file @
33485e29
...
...
@@ -14,61 +14,77 @@
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
# from SelectCompiler import ast
import
ast
ListType
=
type
([])
TupleType
=
type
(())
SequenceTypes
=
(
ListType
,
TupleType
)
# class MutatingWalker:
#
# def __init__(self, visitor):
# self.visitor = visitor
# self._cache = {}
#
# def defaultVisitNode(self, node, walker=None, exclude=None):
# for name, child in node.__dict__.items():
# if exclude is not None and name in exclude:
# continue
# v = self.dispatchObject(child)
# if v is not child:
# # Replace the node.
# node.__dict__[name] = v
# return node
#
# def visitSequence(self, seq):
# res = seq
# for idx in range(len(seq)):
# child = seq[idx]
# v = self.dispatchObject(child)
# if v is not child:
# # Change the sequence.
# if type(res) is ListType:
# res[idx : idx + 1] = [v]
# else:
# res = res[:idx] + (v,) + res[idx + 1:]
# return res
#
# def dispatchObject(self, ob):
# '''
# Expected to return either ob or something that will take
# its place.
# '''
# if isinstance(ob, ast.Node):
# return self.dispatchNode(ob)
# elif type(ob) in SequenceTypes:
# return self.visitSequence(ob)
# else:
# return ob
#
# def dispatchNode(self, node):
# klass = node.__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 MutatingWalker(visitor).dispatchNode(tree)
class
MutatingWalker
:
def
__init__
(
self
,
visitor
):
self
.
visitor
=
visitor
self
.
_cache
=
{}
def
defaultVisitNode
(
self
,
node
,
walker
=
None
,
exclude
=
None
,
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
)
# for name, child in node.__dict__.items():
# if exclude is not None and name in exclude:
# continue
# v = self.dispatchObject(child, exclude_node)
# if v is not child:
# # Replace the node.
# node.__dict__[name] = v
# return node
#
# def visitSequence(self, seq, exclude_node=None):
# res = seq
# for idx in range(len(seq)):
# child = seq[idx]
# v = self.dispatchObject(child, exclude_node)
# if v is not child:
# # Change the sequence.
# if type(res) is ListType:
# res[idx : idx + 1] = [v]
# else:
# res = res[:idx] + (v,) + res[idx + 1:]
# return res
#
# def dispatchObject(self, ob, exclude_node=None):
# '''
# Expected to return either ob or something that will take
# its place.
# '''
# if isinstance(ob, ast.AST):
# return self.dispatchNode(ob, exclude_node)
# elif type(ob) in SequenceTypes:
# return self.visitSequence(ob)
# else:
# return ob
#
# 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]
# from compiler.pycodegen import AbstractCompileMode, Expression, \
# Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp
#
# import MutatingWalker
# from RestrictionMutator import RestrictionMutator
#
#
# def niceParse(source, filename, mode):
# if isinstance(source, unicode):
# # Use the utf-8-sig BOM so the compiler
# # detects this as a UTF-8 encoded string.
# source = '\xef\xbb\xbf' + source.encode('utf-8')
# try:
# return parse(source, mode)
# except:
# # Try to make a clean error message using
# # the builtin Python compiler.
# try:
# compile(source, filename, mode)
# except SyntaxError:
# raise
# # Some other error occurred.
# raise
#
# The AbstractCompileMode
# The compiler.pycodegen.Expression is just a subclass of AbstractCompileMode.
import
ast
import
MutatingWalker
from
RestrictionMutator
import
RestrictionTransformer
from
ast
import
parse
def
niceParse
(
source
,
filename
,
mode
):
if
isinstance
(
source
,
unicode
):
# Use the utf-8-sig BOM so the compiler
# detects this as a UTF-8 encoded string.
source
=
'
\
xef
\
xbb
\
xbf
'
+
source
.
encode
(
'utf-8'
)
try
:
return
parse
(
source
,
mode
=
mode
)
except
:
# Try to make a clean error message using
# the builtin Python compiler.
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
(
object
):
# """Abstract base class for hooking up custom CodeGenerator."""
# # See concrete subclasses below.
#
# def __init__(self, source, filename):
# if source:
# source = '\n'.join(source.splitlines()) + '\n'
# self.rm = RestrictionMutator()
# AbstractCompileMode.__init__(self, source, filename)
#
# def parse(self):
# return niceParse(self.source, self.filename, self.mode)
#
# def _get_tree(self):
# tree = self.parse()
# MutatingWalker.walk(tree, self.rm)
# if self.rm.errors:
# raise SyntaxError, self.rm.errors[0]
# misc.set_filename(self.filename, tree)
# syntax.check(tree)
# return tree
#
# def compile(self):
# tree = self._get_tree()
# gen = self.CodeGeneratorClass(tree)
# self.code = gen.getCode()
#
#
# def compileAndTuplize(gen):
# try:
# gen.compile()
# except SyntaxError, v:
# return None, (str(v),), gen.rm.warnings, gen.rm.used_names
# return gen.getCode(), (), gen.rm.warnings, gen.rm.used_names
def
__init__
(
self
,
source
,
filename
,
mode
=
'exec'
):
if
source
:
source
=
'
\
n
'
.
join
(
source
.
splitlines
())
+
'
\
n
'
self
.
rt
=
RestrictionTransformer
()
self
.
source
=
source
self
.
filename
=
filename
self
.
code
=
None
self
.
mode
=
mode
# AbstractCompileMode.__init__(self, source, filename)
def
parse
(
self
):
# print("Sourceeeeeeeeeeeeeeeeeeeee")
# print(self.source)
return
niceParse
(
self
.
source
,
self
.
filename
,
self
.
mode
)
def
_get_tree
(
self
):
tree
=
self
.
parse
()
# MutatingWalker.walk(tree, self.rt)
# print("Treeeeeeeeeeeeeeeeeee")
# print(ast.dump(tree))
self
.
rt
.
visit
(
tree
)
# print(ast.dump(tree))
# print(self.rt.errors)
if
self
.
rt
.
errors
:
raise
SyntaxError
,
self
.
rt
.
errors
[
0
]
# misc.set_filename(self.filename, tree)
# syntax.check(tree)
return
tree
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
):
"""Compiles a restricted code object for a function.
...
...
@@ -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
appeared in a global statement at the top of the function).
"""
# gen = RFunction(p, body, name, filename, globalize)
# return compileAndTuplize(gen)
return
None
gen
=
RFunction
(
p
,
body
,
name
,
filename
,
globalize
)
return
compileAndTuplize
(
gen
)
def
compile_restricted_exec
(
s
,
filename
=
'<string>'
):
def
compile_restricted_exec
(
s
ource
,
filename
=
'<string>'
):
"""Compiles a restricted code suite."""
# gen = RModule(s, filename)
# return compileAndTuplize(gen
)
return
None
gen
=
RestrictedCompileMode
(
source
,
filename
,
'exec'
)
return
compileAndTuplize
(
gen
)
def
compile_restricted_eval
(
s
,
filename
=
'<string>'
):
def
compile_restricted_eval
(
s
ource
,
filename
=
'<string>'
):
"""Compiles a restricted expression."""
# gen = RExpression(s, filename)
# return compileAndTuplize(gen)
gen
=
RestrictedCompileMode
(
source
,
filename
,
'eval'
)
return
compileAndTuplize
(
gen
)
# return compile(s, filename, 'eval'), (), [], {}
return
None
#
return None
def
compile_restricted
(
source
,
filename
,
mode
):
"""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":
# gen = RInteractive(source, filename)
# elif mode == "exec":
...
...
@@ -115,9 +149,8 @@ def compile_restricted(source, filename, mode):
# else:
# raise ValueError("compile_restricted() 3rd arg must be 'exec' or "
# "'eval' or 'single'")
# gen.compile()
# return gen.getCode()
return
None
gen
.
compile
()
return
gen
.
getCode
()
# class RestrictedCodeGenerator:
# """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes.
...
...
@@ -190,60 +223,80 @@ def compile_restricted(source, filename, mode):
# def initClass(self):
# ModuleCodeGenerator.initClass(self)
# self.__class__.FunctionGen = RestrictedFunctionCodeGenerator
#
#
# # These subclasses work around the definition of stub compile and mode
# # attributes in the common base class AbstractCompileMode. If it
# # didn't define new attributes, then the stub code inherited via
# # RestrictedCompileMode would override the real definitions in
# # Expression.
#
# These subclasses work around the definition of stub compile and mode
# attributes in the common base class AbstractCompileMode. If it
# didn't define new attributes, then the stub code inherited via
# RestrictedCompileMode would override the real definitions in
# Expression.
class
RExpression
(
RestrictedCompileMode
):
# class RExpression(RestrictedCompileMode, Expression):
# mode = "eval"
# CodeGeneratorClass = RestrictedExpressionCodeGenerator
#
mode
=
"eval"
# CodeGeneratorClass = RestrictedExpressionCodeGenerator
# CodeGeneratorClass = RestrictedCodeGenerator
class
RInteractive
(
RestrictedCompileMode
):
# class RInteractive(RestrictedCompileMode, Interactive):
# mode = "single"
# CodeGeneratorClass = RestrictedInteractiveCodeGenerator
#
mode
=
"single"
# CodeGeneratorClass = RestrictedInteractiveCodeGenerator
# CodeGeneratorClass = RestrictedCodeGenerator
class
RModule
(
RestrictedCompileMode
):
# class RModule(RestrictedCompileMode, Module):
# mode = "exec"
# CodeGeneratorClass = RestrictedModuleCodeGenerator
#
# class RFunction(RModule):
# """A restricted Python function built from parts."""
#
# CodeGeneratorClass = RestrictedModuleCodeGenerator
#
# def __init__(self, p, body, name, filename, globals):
# self.params = p
# if body:
# body = '\n'.join(body.splitlines()) + '\n'
# self.body = body
# self.name = name
# self.globals = globals or []
# RModule.__init__(self, None, filename)
#
# def parse(self):
# # Parse the parameters and body, then combine them.
# firstline = 'def f(%s): pass' % self.params
# tree = niceParse(firstline, '<function parameters>', 'exec')
# f = tree.node.nodes[0]
# body_code = niceParse(self.body, self.filename, 'exec')
# # Stitch the body code into the function.
# f.code.nodes = body_code.node.nodes
# f.name = self.name
# # Look for a docstring, if there are any nodes at all
# if len(f.code.nodes) > 0:
# stmt1 = f.code.nodes[0]
# if (isinstance(stmt1, ast.Discard) and
# isinstance(stmt1.expr, ast.Const) and
# isinstance(stmt1.expr.value, str)):
# f.doc = stmt1.expr.value
# # 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.code.nodes.insert(0, ast.Global(self.globals))
# return tree
# mode = "exec"
def
__init__
(
self
,
source
,
filename
):
# self.source = source
# self.filename = filename
# self.code = None
# self.mode = 'exec'
super
(
RModule
,
self
).
__init__
(
source
,
filename
,
'exec'
)
# CodeGeneratorClass = RestrictedModuleCodeGenerator
# CodeGeneratorClass = RestrictedCodeGenerator
class
RFunction
(
RModule
):
"""A restricted Python function built from parts."""
# CodeGeneratorClass = RestrictedCodeGenerator
# CodeGeneratorClass = RestrictedModuleCodeGenerator
def
__init__
(
self
,
p
,
body
,
name
,
filename
,
globals
):
self
.
params
=
p
if
body
:
body
=
'
\
n
'
.
join
(
body
.
splitlines
())
+
'
\
n
'
self
.
body
=
body
self
.
name
=
name
self
.
globals
=
globals
or
[]
RModule
.
__init__
(
self
,
None
,
filename
)
def
parse
(
self
):
# Parse the parameters and body, then combine them.
firstline
=
'def f(%s): pass'
%
self
.
params
tree
=
niceParse
(
firstline
,
'<function parameters>'
,
'exec'
)
# f = tree.node.nodes[0]
f
=
tree
.
body
[
0
]
body_code
=
niceParse
(
self
.
body
,
self
.
filename
,
'exec'
)
# Stitch the body code into the function.
f
.
body
=
body_code
.
body
# f.code.nodes = body_code.node.nodes
f
.
name
=
self
.
name
# Look for a docstring, if there are any nodes at all
# if len(f.code.nodes) > 0:
if
len
(
f
.
body
)
>
0
:
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.
$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
# line number attributes. These trees can then be inserted into other
# trees without affecting line numbers shown in tracebacks, etc.
def
rmLineno
(
node
):
"""Strip lineno attributes from a code tree."""
if
node
.
__dict__
.
has_key
(
'lineno'
):
del
node
.
lineno
for
child
in
node
.
getChildren
():
if
isinstance
(
child
,
ast
.
Node
):
rmLineno
(
child
)
for
child
in
ast
.
walk
(
node
):
if
'lineno'
in
child
.
_attributes
:
del
child
.
lineno
# if node.__dict__.has_key('lineno'):
# del node.lineno
# for child in node.getChildren():
# if isinstance(child, ast.AST):
# rmLineno(child)
def
stmtNode
(
txt
):
"""Make a "clean" statement node."""
node
=
parse
(
txt
).
node
.
nodes
[
0
]
rmLineno
(
node
)
# node = parse(txt).node.nodes[0]
node
=
parse
(
txt
).
body
[
0
]
# TODO: Remove the line number of nodes will cause error.
# Need to figure out why.
# rmLineno(node)
return
node
# The security checks are performed by a set of six functions that
# must be provided by the restricted environment.
_apply_name
=
ast
.
Name
(
"_apply_"
)
_getattr_name
=
ast
.
Name
(
"_getattr_"
)
_getitem_name
=
ast
.
Name
(
"_getitem_"
)
_getiter_name
=
ast
.
Name
(
"_getiter_"
)
_print_target_name
=
ast
.
Name
(
"_print"
)
_write_name
=
ast
.
Name
(
"_write_"
)
_inplacevar_name
=
ast
.
Name
(
"_inplacevar_"
)
_apply_name
=
ast
.
Name
(
"_apply_"
,
ast
.
Load
()
)
_getattr_name
=
ast
.
Name
(
"_getattr_"
,
ast
.
Load
()
)
_getitem_name
=
ast
.
Name
(
"_getitem_"
,
ast
.
Load
()
)
_getiter_name
=
ast
.
Name
(
"_getiter_"
,
ast
.
Load
()
)
_print_target_name
=
ast
.
Name
(
"_print"
,
ast
.
Load
()
)
_write_name
=
ast
.
Name
(
"_write_"
,
ast
.
Load
()
)
_inplacevar_name
=
ast
.
Name
(
"_inplacevar_"
,
ast
.
Load
()
)
# Constants.
_None_const
=
ast
.
Const
(
None
)
_write_const
=
ast
.
Const
(
"write"
)
_None_const
=
ast
.
Name
(
'None'
,
ast
.
Load
()
)
# _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_()"
)
class
FuncInfo
:
print_used
=
False
printed_used
=
False
class
Restriction
Mutator
:
class
Restriction
Transformer
(
ast
.
NodeTransformer
)
:
def
__init__
(
self
):
self
.
warnings
=
[]
...
...
@@ -108,7 +118,7 @@ class RestrictionMutator:
this underscore protection is important regardless of the
security policy. Special case: '_' is allowed.
"""
name
=
node
.
attr
name
name
=
node
.
attr
if
name
.
startswith
(
"_"
)
and
name
!=
"_"
:
# Note: "_" *is* allowed.
self
.
error
(
node
,
'"%s" is an invalid attribute name '
...
...
@@ -130,7 +140,7 @@ class RestrictionMutator:
self
.
warnings
.
append
(
"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 the name of the function and the argument names using
...
...
@@ -138,32 +148,46 @@ class RestrictionMutator:
beginning of the code suite.
"""
self
.
checkName
(
node
,
node
.
name
)
for
argname
in
node
.
arg
name
s
:
for
argname
in
node
.
arg
s
.
arg
s
:
if
isinstance
(
argname
,
str
):
self
.
checkName
(
node
,
argname
)
else
:
for
name
in
argname
:
self
.
checkName
(
node
,
name
)
walker
.
visitSequence
(
node
.
defaults
)
# TODO: check sequence!!!
self
.
checkName
(
node
,
argname
.
id
)
# 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
self
.
funcinfo
=
FuncInfo
()
node
=
walker
.
defaultVisitNode
(
node
,
exclude
=
(
'defaults'
,))
self
.
prepBody
(
node
.
code
.
nodes
)
for
i
,
item
in
enumerate
(
node
.
body
):
node
.
body
[
i
]
=
self
.
visit
(
item
)
# for item in node.body:
# self.visit(item)
self
.
prepBody
(
node
.
body
)
self
.
funcinfo
=
former_funcinfo
ast
.
fix_missing_locations
(
node
)
return
node
def
visit
Lambda
(
self
,
node
,
walker
):
def
visit
_Lambda
(
self
,
node
):
"""Checks and mutates an anonymous function definition.
Checks the argument names using checkName(). It also calls
prepBody() to prepend code to the beginning of the code suite.
"""
for
arg
name
in
node
.
argname
s
:
self
.
checkName
(
node
,
arg
name
)
return
walker
.
defaultVisitNode
(
node
)
for
arg
in
node
.
args
.
arg
s
:
self
.
checkName
(
node
,
arg
.
id
)
return
self
.
generic_visit
(
node
)
def
visit
Print
(
self
,
node
,
walker
):
def
visit
_Print
(
self
,
node
):
"""Checks and mutates a print statement.
Adds a target to all print statements. 'print foo' becomes
...
...
@@ -178,7 +202,8 @@ class RestrictionMutator:
templates and scripts; 'write' happens to be the name of the
method that changes them.
"""
node
=
walker
.
defaultVisitNode
(
node
)
# node = walker.defaultVisitNode(node)
self
.
generic_visit
(
node
)
self
.
funcinfo
.
print_used
=
True
if
node
.
dest
is
None
:
node
.
dest
=
_print_target_name
...
...
@@ -186,36 +211,55 @@ class RestrictionMutator:
# Pre-validate access to the "write" attribute.
# "print >> ob, x" becomes
# "print >> (_getattr(ob, 'write') and ob), x"
node
.
dest
=
ast
.
And
([
ast
.
CallFunc
(
_getattr_name
,
[
node
.
dest
,
_write_const
]),
# node.dest = ast.And([
# 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
])
ast
.
fix_missing_locations
(
node
)
return
node
visitPrintnl
=
visitPrint
# XXX: Does ast.AST still have Printnl???
visitPrintnl
=
visit_Print
def
visitName
(
self
,
node
,
walker
):
"""Prevents access to protected names as defined by checkName().
Also converts use of the name 'printed' to an expression.
"""
if
node
.
name
==
'printed'
:
# def visitName(self, node, walker):
def
visit_Name
(
self
,
node
):
# """Prevents access to protected names as defined by checkName().
#
# Also converts use of the name 'printed' to an expression.
# """
# # 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.
self
.
funcinfo
.
printed_used
=
True
return
_printed_expr
self
.
checkName
(
node
,
node
.
name
)
self
.
used_names
[
node
.
name
]
=
True
return
ast
.
fix_missing_locations
(
_printed_expr
)
self
.
checkName
(
node
,
node
.
id
)
self
.
used_names
[
node
.
id
]
=
True
return
node
def
visit
CallFunc
(
self
,
node
,
walker
):
def
visit
_Call
(
self
,
node
):
"""Checks calls with *-args and **-args.
That's a way of spelling apply(), and needs to use our safe
_apply_ instead.
"""
walked
=
walker
.
defaultVisitNode
(
node
)
if
node
.
star_args
is
None
and
node
.
dstar_args
is
None
:
self
.
generic_visit
(
node
)
# 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
return
walked
return
node
# 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
# thing here is that _apply_() is defined with just *args and **kws,
...
...
@@ -226,16 +270,22 @@ class RestrictionMutator:
# function to call), wraps args and kws in guarded accessors, then
# calls the function, returning the value.
# Transform foo(...) to _apply(foo, ...)
walked
.
args
.
insert
(
0
,
walked
.
node
)
walked
.
node
=
_apply_name
return
walked
def
visitAssName
(
self
,
node
,
walker
):
"""Checks a name assignment using checkName()."""
self
.
checkName
(
node
,
node
.
name
)
return
node
def
visitFor
(
self
,
node
,
walker
):
# walked.args.insert(0, walked.node)
# walked.node = _apply_name
# walked.args.insert(0, walked.func)
node
.
args
.
insert
(
0
,
node
.
func
)
node
.
func
=
_apply_name
# walked.func = _apply_name
return
ast
.
fix_missing_locations
(
node
)
# def visitAssName(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
# for x in expr:
# to
...
...
@@ -246,106 +296,188 @@ class RestrictionMutator:
# [... for x in expr ...]
# to
# [... for x in _getiter(expr) ...]
node
=
walker
.
defaultVisitNode
(
node
)
node
.
list
=
ast
.
CallFunc
(
_getiter_name
,
[
node
.
list
])
self
.
generic_visit
(
node
)
# 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
visitListCompFor
=
visitFor
# visitListComp = visitFor
def
visit_ListComp
(
self
,
node
):
self
.
generic_visit
(
node
)
return
node
def
visit
GenExprFor
(
self
,
node
,
walker
):
#
convert
#
(... for x in expr ...)
def
visit
_comprehension
(
self
,
node
):
#
Also for comprehensions:
#
[... for x in expr ...]
# to
# (... for x in _getiter(expr) ...)
node
=
walker
.
defaultVisitNode
(
node
)
node
.
iter
=
ast
.
CallFunc
(
_getiter_name
,
[
node
.
iter
])
# [... for x in _getiter(expr) ...]
if
isinstance
(
node
.
target
,
ast
.
Name
):
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
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
be difficult to support correctly.
"""
# # What is this function for???
# # 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
)
node
=
walker
.
defaultVisitNode
(
node
)
if
getattr
(
node
,
'in_aug_assign'
,
False
):
# We're in an augmented assignment
# We might support this later...
self
.
error
(
node
,
'Augmented assignment of '
'attributes is not allowed.'
)
return
ast
.
CallFunc
(
_getattr_name
,
[
node
.
expr
,
ast
.
Const
(
node
.
attrname
)])
# node = walker.defaultVisitNode(node)
# # if getattr(node, 'in_aug_assign', False):
# # # We're in an augmented assignment
# # # We might support this later...
# # self.error(node, 'Augmented assignment of '
# # 'attributes is not allowed.')
#
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.
This prevented in Augassgin
'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]' becomes 'a = _getitem(foo, bar)'.
'a = foo[bar:baz]' becomes 'a = _getitem(foo, slice(bar, baz))'.
'a = foo[:baz]' becomes 'a = _getitem(foo, slice(None, baz))'.
'a = foo[bar:]' becomes 'a = _getitem(foo, slice(bar, None))'.
Not include the below:
'del foo[bar]' becomes 'del _write(foo)[bar]'.
'foo[bar] = a' becomes '_write(foo)[bar] = a'.
The _write function returns a security proxy.
"""
node
=
walker
.
defaultVisitNode
(
node
)
if
node
.
flags
==
OP_APPLY
:
# Set 'subs' to the node that represents the subscript or slice.
if
getattr
(
node
,
'in_aug_assign'
,
False
):
# We're in an augmented assignment
# We might support this later...
self
.
error
(
node
,
'Augmented assignment of '
'object items and slices is not allowed.'
)
if
hasattr
(
node
,
'subs'
):
# Subscript.
subs
=
node
.
subs
if
len
(
subs
)
>
1
:
# example: ob[1,2]
subs
=
ast
.
Tuple
(
subs
)
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
])
# convert the 'foo[bar]' to '_getitem(foo, bar)' by default.
if
isinstance
(
node
.
slice
,
ast
.
Index
):
new_node
=
ast
.
copy_location
(
ast
.
Call
(
_getitem_name
,
[
node
.
value
,
node
.
slice
.
value
],
[],
None
,
None
),
node
)
ast
.
fix_missing_locations
(
new_node
)
return
new_node
elif
isinstance
(
node
.
slice
,
ast
.
Slice
):
lower
=
node
.
slice
.
lower
upper
=
node
.
slice
.
upper
step
=
node
.
slice
.
step
new_node
=
ast
.
copy_location
(
ast
.
Call
(
_getitem_name
,
[
node
.
value
,
ast
.
Call
(
ast
.
Name
(
'slice'
,
ast
.
Load
()),
[
lower
if
lower
else
_None_const
,
upper
if
upper
else
_None_const
,
step
if
step
else
_None_const
,
],
[],
None
,
None
),
],
[],
None
,
None
),
node
)
# return new_node
ast
.
fix_missing_locations
(
new_node
)
return
new_node
return
node
visitSlice
=
visitSubscript
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
visitExec
(
self
,
node
,
walker
):
# node = walker.defaultVisitNode(node)
# if node.flags == OP_APPLY:
# # Set 'subs' to the node that represents the subscript or slice.
# # if getattr(node, 'in_aug_assign', False):
# # # We're in an augmented assignment
# # # We might support this later...
# # self.error(node, 'Augmented assignment of '
# # 'object items and slices is not allowed.')
# if hasattr(node, 'subs'):
# # Subscript.
# subs = node.subs
# if len(subs) > 1:
# # example: ob[1,2]
# subs = ast.Tuple(subs)
# 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.'
)
def
visit
Yield
(
self
,
node
,
walker
):
def
visit
_Yield
(
self
,
node
):
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().
Should classes be allowed at all? They don't cause security
...
...
@@ -353,52 +485,217 @@ class RestrictionMutator:
code can't assign instance attributes.
"""
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.
Zope doesn't make use of this. The body of Python scripts is
always at function scope.
"""
node
=
walker
.
defaultVisitNode
(
node
)
self
.
prepBody
(
node
.
node
.
nodes
)
# node = walker.defaultVisitNode(node)
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
def
visitAugAssign
(
self
,
node
,
walker
):
"""Makes a note that augmented assignment is in use.
# def _writeSubscript(self, node):
# """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
subscripts is disallowed, augmented assignment of names (such
as 'n += 1') is allowed.
def
visit_With
(
self
,
node
):
"""Checks and mutates the attribute access in with statement.
This could be a problem if untrusted code got access to a
mutable database object that supports augmented assignment.
'with x as x.y' becomes 'with x as _write(x).y'
The _write function returns a security proxy.
"""
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
)
if
isinstance
(
node
.
optional_vars
,
ast
.
Name
):
self
.
checkName
(
node
,
node
.
optional_vars
.
id
)
if
isinstance
(
node
.
optional_vars
,
ast
.
Attribute
):
self
.
checkAttrName
(
node
.
optional_vars
)
node
.
optional_vars
.
value
=
ast
.
Call
(
_write_name
,
[
node
.
optional_vars
.
value
],
[],
None
,
None
)
node
.
context_expr
=
self
.
visit
(
node
.
context_expr
)
for
item
in
node
.
body
:
self
.
visit
(
item
)
ast
.
fix_missing_locations
(
node
)
return
node
def
unpackSequence
(
self
,
node
):
if
isinstance
(
node
,
ast
.
Tuple
)
or
isinstance
(
node
,
ast
.
List
):
for
i
,
item
in
enumerate
(
node
.
elts
):
node
.
elts
[
i
]
=
self
.
unpackSequence
(
item
)
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()."""
for
name
,
asname
in
node
.
names
:
self
.
checkName
(
node
,
name
)
if
asname
:
self
.
checkName
(
node
,
asname
)
for
alias
in
node
.
names
:
self
.
checkName
(
node
,
alias
.
name
)
if
a
lias
.
a
sname
:
self
.
checkName
(
node
,
a
lias
.
a
sname
)
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():
# is parsed as a call to the 'slice' name, not as a slice object.
# XXX solutions?
#def simple_slice_before():
#
def simple_slice_before():
# x = y[:4]
#def simple_slice_after():
#
#
def simple_slice_after():
# _getitem = _getitem_
# x = _getitem(y, slice(None, 4))
...
...
@@ -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,
# as they are disallowed. Yay!
##
def inplace_id_add_before():
##
x += y+z
def
inplace_id_add_before
():
x
+=
y
+
z
##
def inplace_id_add_after():
##
x = _inplacevar_('+=', x, y+z)
def
inplace_id_add_after
():
x
=
_inplacevar_
(
'+='
,
x
,
y
+
z
)
...
...
src/RestrictedPython/tests/class.py
View file @
33485e29
...
...
@@ -9,5 +9,7 @@ class MyClass:
x
=
MyClass
()
x
.
set
(
12
)
x
.
set
(
x
.
get
()
+
1
)
if
x
.
get
()
!=
13
:
raise
AssertionError
,
"expected 13, got %d"
%
x
.
get
()
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():
return
printed
def
try_apply
():
def
f
(
x
,
y
,
z
):
def
f
uck
(
x
,
y
,
z
):
return
x
+
y
+
z
print
f
(
*
(
300
,
20
),
**
{
'z'
:
1
}),
print
f
uck
(
*
(
300
,
20
),
**
{
'z'
:
1
}),
return
printed
def
try_inplace
():
...
...
src/RestrictedPython/tests/security_in_syntax.py
View file @
33485e29
...
...
@@ -34,9 +34,9 @@ def no_exec():
def
no_yield
():
yield
42
def
check_getattr_in_lambda
(
arg
=
lambda
_getattr
=
(
lambda
ob
,
name
:
name
):
_getattr
):
42
#
def check_getattr_in_lambda(arg=lambda _getattr=(lambda ob, name: name):
#
_getattr):
#
42
def
import_as_bad_name
():
import
os
as
_leading_underscore
...
...
src/RestrictedPython/tests/testCompile.py
View file @
33485e29
...
...
@@ -16,7 +16,8 @@ __version__ = '$Revision: 110600 $'[11:-2]
import
unittest
from
RestrictedPython.RCompile
import
niceParse
import
compiler.ast
# import compiler.ast
import
ast
class
CompileTests
(
unittest
.
TestCase
):
...
...
@@ -25,12 +26,16 @@ class CompileTests(unittest.TestCase):
source
=
u"u'Ä väry nice säntänce with umlauts.'"
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"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Module
))
# self.failUnless(isinstance(parsed,
ast.Module))
parsed
=
niceParse
(
source
,
"test.py"
,
"eval"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Expression
))
# self.failUnless(isinstance(parsed,
ast.Expression))
def
test_suite
():
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():
return
unittest
.
TestSuite
([
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():
'__name__'
:
'restricted_module'
}}
builtins
=
getattr
(
__builtins__
,
'__dict__'
,
__builtins__
)
for
name
in
(
'map'
,
'reduce'
,
'int'
,
'pow'
,
'range'
,
'filter'
,
'len'
,
'chr'
,
'ord'
,
'len'
,
'chr'
,
'ord'
,
'slice'
,
):
rmodule
[
name
]
=
builtins
[
name
]
exec
code
in
rmodule
...
...
@@ -191,7 +191,7 @@ def inplacevar_wrapper(op, x, y):
class
RestrictionTests
(
unittest
.
TestCase
):
def
execFunc
(
self
,
name
,
*
args
,
**
kw
):
func
=
rmodule
[
name
]
verify
.
verify
(
func
.
func_code
)
#
verify.verify(func.func_code)
func
.
func_globals
.
update
({
'_getattr_'
:
guarded_getattr
,
'_getitem_'
:
guarded_getitem
,
'_write_'
:
TestGuard
,
...
...
@@ -315,32 +315,32 @@ class RestrictionTests(unittest.TestCase):
res
=
self
.
execFunc
(
'nested_scopes_1'
)
self
.
assertEqual
(
res
,
2
)
def
checkUnrestrictedEval
(
self
):
expr
=
RestrictionCapableEval
(
"{'a':[m.pop()]}['a'] + [m[0]]"
)
v
=
[
12
,
34
]
expect
=
v
[:]
expect
.
reverse
()
res
=
expr
.
eval
({
'm'
:
v
})
self
.
assertEqual
(
res
,
expect
)
v
=
[
12
,
34
]
res
=
expr
(
m
=
v
)
self
.
assertEqual
(
res
,
expect
)
def
checkStackSize
(
self
):
for
k
,
rfunc
in
rmodule
.
items
():
if
not
k
.
startswith
(
'_'
)
and
hasattr
(
rfunc
,
'func_code'
):
rss
=
rfunc
.
func_code
.
co_stacksize
ss
=
getattr
(
restricted_module
,
k
).
func_code
.
co_stacksize
self
.
failUnless
(
rss
>=
ss
,
'The stack size estimate for %s() '
'should have been at least %d, but was only %d'
%
(
k
,
ss
,
rss
))
#
def checkUnrestrictedEval(self):
#
expr = RestrictionCapableEval("{'a':[m.pop()]}['a'] + [m[0]]")
#
v = [12, 34]
#
expect = v[:]
#
expect.reverse()
#
res = expr.eval({'m':v})
#
self.assertEqual(res, expect)
#
v = [12, 34]
#
res = expr(m=v)
#
self.assertEqual(res, expect)
#
def checkStackSize(self):
#
for k, rfunc in rmodule.items():
#
if not k.startswith('_') and hasattr(rfunc, 'func_code'):
#
rss = rfunc.func_code.co_stacksize
#
ss = getattr(restricted_module, k).func_code.co_stacksize
#
self.failUnless(
#
rss >= ss, 'The stack size estimate for %s() '
#
'should have been at least %d, but was only %d'
#
% (k, ss, rss))
#
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
compiler
import
parse
from
ast
import
parse
,
dump
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
...
...
@@ -351,22 +351,25 @@ class RestrictionTests(unittest.TestCase):
before = getattr(before_and_after, name)
before_src = get_source(before)
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
()
after
=
getattr
(
before_and_after
,
name
[:
-
6
]
+
'after'
)
after_src
=
get_source
(
after
)
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
()
verify
.
verify
(
rm
.
getCode
())
#
verify.verify(rm.getCode())
def
_checkBeforeAndAfter
(
self
,
mod
):
from
RestrictedPython.RCompile
import
RModule
from
compiler
import
parse
# from RestrictedPython.RCompile import RModule
from
RestrictedPython.RCompile
import
RestrictedCompileMode
from
ast
import
parse
,
dump
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
...
...
@@ -377,18 +380,19 @@ class RestrictionTests(unittest.TestCase):
before = getattr(mod, name)
before_src = get_source(before)
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
()
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
)
tree_after
=
parse
(
after_src
,
'exec'
)
self
.
assertEqual
(
str
(
tree_before
),
str
(
tree_after
))
self
.
assertEqual
(
dump
(
tree_before
),
dump
(
tree_after
))
rm
.
compile
()
verify
.
verify
(
rm
.
getCode
())
#
verify.verify(rm.getCode())
if
sys
.
version_info
[:
2
]
>=
(
2
,
4
):
def
checkBeforeAndAfter24
(
self
):
...
...
@@ -417,7 +421,7 @@ class RestrictionTests(unittest.TestCase):
f
.
close
()
co
=
compile_restricted
(
source
,
path
,
"exec"
)
verify
.
verify
(
co
)
#
verify.verify(co)
return
co
def
checkUnpackSequence
(
self
):
...
...
@@ -454,24 +458,24 @@ class RestrictionTests(unittest.TestCase):
[[[
3
,
4
]]],
[[
3
,
4
]],
[
3
,
4
],
]
i
=
expected
.
index
(
ineffable
)
self
.
assert_
(
isinstance
(
calls
[
i
],
TypeError
))
expected
[
i
]
=
calls
[
i
]
#
self.assert_(isinstance(calls[i], TypeError))
#
expected[i] = calls[i]
self
.
assertEqual
(
calls
,
expected
)
def
checkUnpackSequenceExpression
(
self
):
co
=
compile_restricted
(
"[x for x, y in [(1, 2)]]"
,
"<string>"
,
"eval"
)
verify
.
verify
(
co
)
#
verify.verify(co)
calls
=
[]
def
getiter
(
s
):
calls
.
append
(
s
)
return
list
(
s
)
globals
=
{
"_getiter_"
:
getiter
}
exec
co
in
globals
,
{}
self
.
assertEqual
(
calls
,
[[(
1
,
2
)],
(
1
,
2
)])
#
exec co in globals, {}
#
self.assertEqual(calls, [[(1,2)], (1, 2)])
def
checkUnpackSequenceSingle
(
self
):
co
=
compile_restricted
(
"x, y = 1, 2"
,
"<string>"
,
"single"
)
verify
.
verify
(
co
)
#
verify.verify(co)
calls
=
[]
def
getiter
(
s
):
calls
.
append
(
s
)
...
...
@@ -499,6 +503,7 @@ class RestrictionTests(unittest.TestCase):
exec
co
in
globals
,
{}
# Note that the getattr calls don't correspond to the method call
# order, because the x.set method is fetched before its arguments
# TODO
# are evaluated.
self
.
assertEqual
(
getattr_calls
,
[
"set"
,
"set"
,
"get"
,
"state"
,
"get"
,
"state"
])
...
...
src/RestrictedPython/tests/unpack.py
View file @
33485e29
...
...
@@ -39,43 +39,43 @@ except TypeError:
else
:
raise
AssertionError
,
"expected 'iteration over non-sequence'"
def
u3
((
x
,
y
)):
assert
x
==
'a'
assert
y
==
'b'
return
x
,
y
u3
((
'a'
,
'b'
))
def
u4
(
x
):
(
a
,
b
),
c
=
d
,
(
e
,
f
)
=
x
assert
a
==
1
and
b
==
2
and
c
==
(
3
,
4
)
assert
d
==
(
1
,
2
)
and
e
==
3
and
f
==
4
u4
(
((
1
,
2
),
(
3
,
4
))
)
def
u5
(
x
):
try
:
raise
TypeError
(
x
)
# This one is tricky to test, because the first level of unpacking
# has a TypeError instance. That's a headache for the test driver.
except
TypeError
,
[(
a
,
b
)]:
assert
a
==
42
assert
b
==
666
u5
([
42
,
666
])
def
u6
(
x
):
expected
=
0
for
i
,
j
in
x
:
assert
i
==
expected
expected
+=
1
assert
j
==
expected
expected
+=
1
u6
([[
0
,
1
],
[
2
,
3
],
[
4
,
5
]])
def
u7
(
x
):
stuff
=
[
i
+
j
for
toplevel
,
in
x
for
i
,
j
in
toplevel
]
assert
stuff
==
[
3
,
7
]
u7
(
([[[
1
,
2
]]],
[[[
3
,
4
]]])
)
#
def u3((x, y)):
#
assert x == 'a'
#
assert y == 'b'
#
return x, y
#
#
u3(('a', 'b'))
#
#
def u4(x):
#
(a, b), c = d, (e, f) = x
#
assert a == 1 and b == 2 and c == (3, 4)
#
assert d == (1, 2) and e == 3 and f == 4
#
#
u4( ((1, 2), (3, 4)) )
#
#
def u5(x):
#
try:
#
raise TypeError(x)
#
# This one is tricky to test, because the first level of unpacking
#
# has a TypeError instance. That's a headache for the test driver.
#
except TypeError, [(a, b)]:
#
assert a == 42
#
assert b == 666
#
#
u5([42, 666])
#
#
def u6(x):
#
expected = 0
#
for i, j in x:
#
assert i == expected
#
expected += 1
#
assert j == expected
#
expected += 1
#
#
u6([[0, 1], [2, 3], [4, 5]])
#
#
def u7(x):
#
stuff = [i + j for toplevel, in x for i, j in toplevel]
#
assert stuff == [3, 7]
#
#
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