Change safe_eval to a strict white list

release1.6.4
James Cammarata 11 years ago
parent dffea7f877
commit b8b96b2109

@ -31,7 +31,7 @@ def mk_boolean(value):
else: else:
return False return False
def get_config(p, section, key, env_var, default, boolean=False, integer=False, floating=False): def get_config(p, section, key, env_var, default, boolean=False, integer=False, floating=False, islist=False):
''' return a configuration variable with casting ''' ''' return a configuration variable with casting '''
value = _get_config(p, section, key, env_var, default) value = _get_config(p, section, key, env_var, default)
if boolean: if boolean:
@ -40,6 +40,8 @@ def get_config(p, section, key, env_var, default, boolean=False, integer=False,
return int(value) return int(value)
if value and floating: if value and floating:
return float(value) return float(value)
if value and islist:
return [x.strip() for x in value.split(',')]
return value return value
def _get_config(p, section, key, env_var, default): def _get_config(p, section, key, env_var, default):
@ -129,12 +131,12 @@ DEFAULT_SUDO_FLAGS = get_config(p, DEFAULTS, 'sudo_flags', 'ANSIBLE_SUDO_
DEFAULT_HASH_BEHAVIOUR = get_config(p, DEFAULTS, 'hash_behaviour', 'ANSIBLE_HASH_BEHAVIOUR', 'replace') DEFAULT_HASH_BEHAVIOUR = get_config(p, DEFAULTS, 'hash_behaviour', 'ANSIBLE_HASH_BEHAVIOUR', 'replace')
DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBLE_JINJA2_EXTENSIONS', None) DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBLE_JINJA2_EXTENSIONS', None)
DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh') DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh')
DEFAULT_SU_EXE = get_config(p, DEFAULTS, 'su_exe', 'ANSIBLE_SU_EXE', 'su') DEFAULT_SU_EXE = get_config(p, DEFAULTS, 'su_exe', 'ANSIBLE_SU_EXE', 'su')
DEFAULT_SU = get_config(p, DEFAULTS, 'su', 'ANSIBLE_SU', False, boolean=True) DEFAULT_SU = get_config(p, DEFAULTS, 'su', 'ANSIBLE_SU', False, boolean=True)
DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAGS', '') DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAGS', '')
DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root') DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root')
DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True) DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True)
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower() DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '/usr/share/ansible_plugins/action_plugins') DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '/usr/share/ansible_plugins/action_plugins')
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '/usr/share/ansible_plugins/callback_plugins') DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '/usr/share/ansible_plugins/callback_plugins')
@ -152,6 +154,7 @@ DEFAULT_UNDEFINED_VAR_BEHAVIOR = get_config(p, DEFAULTS, 'error_on_undefined_var
HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', 'ANSIBLE_HOST_KEY_CHECKING', True, boolean=True) HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', 'ANSIBLE_HOST_KEY_CHECKING', True, boolean=True)
SYSTEM_WARNINGS = get_config(p, DEFAULTS, 'system_warnings', 'ANSIBLE_SYSTEM_WARNINGS', True, boolean=True) SYSTEM_WARNINGS = get_config(p, DEFAULTS, 'system_warnings', 'ANSIBLE_SYSTEM_WARNINGS', True, boolean=True)
DEPRECATION_WARNINGS = get_config(p, DEFAULTS, 'deprecation_warnings', 'ANSIBLE_DEPRECATION_WARNINGS', True, boolean=True) DEPRECATION_WARNINGS = get_config(p, DEFAULTS, 'deprecation_warnings', 'ANSIBLE_DEPRECATION_WARNINGS', True, boolean=True)
DEFAULT_CALLABLE_WHITELIST = get_config(p, DEFAULTS, 'callable_whitelist', 'ANSIBLE_CALLABLE_WHITELIST', [], islist=True)
# CONNECTION RELATED # CONNECTION RELATED
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None) ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)

@ -1067,21 +1067,31 @@ def safe_eval(expr, locals={}, include_exceptions=False):
) )
) )
# builtin functions that are not safe to call # builtin functions that are safe to call
INVALID_CALLS = ( BUILTIN_WHITELIST = [
'classmethod', 'compile', 'delattr', 'eval', 'execfile', 'file', 'abs', 'all', 'any', 'basestring', 'bin', 'bool', 'buffer', 'bytearray',
'filter', 'help', 'input', 'object', 'open', 'raw_input', 'reduce', 'bytes', 'callable', 'chr', 'cmp', 'coerce', 'complex', 'copyright', 'credits',
'reload', 'repr', 'setattr', 'staticmethod', 'super', 'type', 'dict', 'dir', 'divmod', 'enumerate', 'exit', 'float', 'format', 'frozenset',
) 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'int', 'intern',
'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long',
'map', 'max', 'memoryview', 'min', 'next', 'oct', 'ord', 'pow', 'print',
'property', 'quit', 'range', 'reversed', 'round', 'set', 'slice', 'sorted',
'str', 'sum', 'tuple', 'unichr', 'unicode', 'vars', 'xrange', 'zip',
]
filter_list = []
for filter in filter_loader.all():
filter_list.extend(filter.filters().keys())
CALL_WHITELIST = BUILTIN_WHITELIST + filter_list + C.DEFAULT_CALLABLE_WHITELIST
class CleansingNodeVisitor(ast.NodeVisitor): class CleansingNodeVisitor(ast.NodeVisitor):
def generic_visit(self, node): def generic_visit(self, node):
if type(node) not in SAFE_NODES: if type(node) not in SAFE_NODES:
#raise Exception("invalid expression (%s) type=%s" % (expr, type(node)))
raise Exception("invalid expression (%s)" % expr) raise Exception("invalid expression (%s)" % expr)
super(CleansingNodeVisitor, self).generic_visit(node) super(CleansingNodeVisitor, self).generic_visit(node)
def visit_Call(self, call): def visit_Call(self, call):
if call.func.id in INVALID_CALLS: if call.func.id not in CALL_WHITELIST:
raise Exception("invalid function: %s" % call.func.id) raise Exception("invalid function: %s" % call.func.id)
if not isinstance(expr, basestring): if not isinstance(expr, basestring):

Loading…
Cancel
Save