Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
ansible
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
ansible
Commits
998793fd
Commit
998793fd
authored
Mar 31, 2014
by
James Cammarata
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes to safe_eval
parent
157b7835
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
72 additions
and
27 deletions
+72
-27
lib/ansible/utils/__init__.py
+72
-27
No files found.
lib/ansible/utils/__init__.py
View file @
998793fd
...
...
@@ -29,6 +29,7 @@ from ansible.utils.plugins import *
from
ansible.utils
import
template
from
ansible.callbacks
import
display
import
ansible.constants
as
C
import
ast
import
time
import
StringIO
import
stat
...
...
@@ -945,51 +946,95 @@ def is_list_of_strings(items):
return
False
return
True
def
safe_eval
(
str
,
locals
=
None
,
include_exceptions
=
False
):
def
safe_eval
(
expr
,
locals
=
{}
,
include_exceptions
=
False
):
'''
this is intended for allowing things like:
with_items: a_list_variable
where Jinja2 would return a string
but we do not want to allow it to call functions (outside of Jinja2, where
the env is constrained)
Based on:
http://stackoverflow.com/questions/12523516/using-ast-and-whitelists-to-make-pythons-eval-safe
'''
# FIXME: is there a more native way to do this?
def
is_set
(
var
):
return
not
var
.
startswith
(
"$"
)
and
not
'{{'
in
var
# this is the whitelist of AST nodes we are going to
# allow in the evaluation. Any node type other than
# those listed here will raise an exception in our custom
# visitor class defined below.
SAFE_NODES
=
set
(
(
ast
.
Expression
,
ast
.
Compare
,
ast
.
Str
,
ast
.
List
,
ast
.
Tuple
,
ast
.
Dict
,
ast
.
Call
,
ast
.
Load
,
ast
.
BinOp
,
ast
.
UnaryOp
,
ast
.
Num
,
ast
.
Name
,
ast
.
Add
,
ast
.
Sub
,
ast
.
Mult
,
ast
.
Div
,
)
)
# AST node types were expanded after 2.6
if
not
sys
.
version
.
startswith
(
'2.6'
):
SAFE_NODES
.
union
(
set
(
(
ast
.
Set
,)
)
)
def
is_unset
(
var
):
return
var
.
startswith
(
"$"
)
or
'{{'
in
var
# builtin functions that are not safe to call
INVALID_CALLS
=
(
'classmethod'
,
'compile'
,
'delattr'
,
'eval'
,
'execfile'
,
'file'
,
'filter'
,
'help'
,
'input'
,
'object'
,
'open'
,
'raw_input'
,
'reduce'
,
'reload'
,
'repr'
,
'setattr'
,
'staticmethod'
,
'super'
,
'type'
,
)
# do not allow method calls to modules
if
not
isinstance
(
str
,
basestring
):
class
CleansingNodeVisitor
(
ast
.
NodeVisitor
):
def
generic_visit
(
self
,
node
):
if
type
(
node
)
not
in
SAFE_NODES
:
#raise Exception("invalid expression (%s) type=%s" % (expr, type(node)))
raise
Exception
(
"invalid expression (
%
s)"
%
expr
)
super
(
CleansingNodeVisitor
,
self
)
.
generic_visit
(
node
)
def
visit_Call
(
self
,
call
):
if
call
.
func
.
id
in
INVALID_CALLS
:
raise
Exception
(
"invalid function:
%
s"
%
call
.
func
.
id
)
if
not
isinstance
(
expr
,
basestring
):
# already templated to a datastructure, perhaps?
if
include_exceptions
:
return
(
str
,
None
)
return
str
if
re
.
search
(
r'\w\.\w+\('
,
str
):
if
include_exceptions
:
return
(
str
,
None
)
return
str
# do not allow imports
if
re
.
search
(
r'import \w+'
,
str
):
if
include_exceptions
:
return
(
str
,
None
)
return
str
return
(
expr
,
None
)
return
expr
try
:
result
=
None
if
not
locals
:
result
=
eval
(
str
)
else
:
result
=
eval
(
str
,
None
,
locals
)
parsed_tree
=
ast
.
parse
(
expr
,
mode
=
'eval'
)
cnv
=
CleansingNodeVisitor
()
cnv
.
visit
(
parsed_tree
)
compiled
=
compile
(
parsed_tree
,
expr
,
'eval'
)
result
=
eval
(
compiled
,
{},
locals
)
if
include_exceptions
:
return
(
result
,
None
)
else
:
return
result
except
SyntaxError
,
e
:
# special handling for syntax errors, we just return
# the expression string back as-is
if
include_exceptions
:
return
(
expr
,
None
)
return
expr
except
Exception
,
e
:
if
include_exceptions
:
return
(
st
r
,
e
)
return
st
r
return
(
exp
r
,
e
)
return
exp
r
def
listify_lookup_plugin_terms
(
terms
,
basedir
,
inject
):
...
...
@@ -1001,7 +1046,7 @@ def listify_lookup_plugin_terms(terms, basedir, inject):
# with_items: {{ alist }}
stripped
=
terms
.
strip
()
if
not
(
stripped
.
startswith
(
'{'
)
or
stripped
.
startswith
(
'['
))
and
not
stripped
.
startswith
(
"/"
):
if
not
(
stripped
.
startswith
(
'{'
)
or
stripped
.
startswith
(
'['
))
and
not
stripped
.
startswith
(
"/"
)
and
not
stripped
.
startswith
(
'set(['
)
:
# if not already a list, get ready to evaluate with Jinja2
# not sure why the "/" is in above code :)
try
:
...
...
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