Validate variable names when loading 'vars:' blocks

TODO: add this to VariableManager to validate vars loaded from files too

Fixes #12022
This commit is contained in:
James Cammarata 2015-08-28 11:35:43 -04:00
parent 266a069a73
commit 5a5b9f211b
3 changed files with 47 additions and 34 deletions

View file

@ -37,7 +37,7 @@ from ansible.playbook.attribute import Attribute, FieldAttribute
from ansible.template import Templar from ansible.template import Templar
from ansible.utils.boolean import boolean from ansible.utils.boolean import boolean
from ansible.utils.debug import debug from ansible.utils.debug import debug
from ansible.utils.vars import combine_vars from ansible.utils.vars import combine_vars, isidentifier
from ansible.template import template from ansible.template import template
class Base: class Base:
@ -379,14 +379,21 @@ class Base:
list into a single dictionary. list into a single dictionary.
''' '''
def _validate_variable_keys(ds):
for key in ds:
if not isidentifier(key):
raise TypeError("%s is not a valid variable name" % key)
try: try:
if isinstance(ds, dict): if isinstance(ds, dict):
_validate_variable_keys(ds)
return ds return ds
elif isinstance(ds, list): elif isinstance(ds, list):
all_vars = dict() all_vars = dict()
for item in ds: for item in ds:
if not isinstance(item, dict): if not isinstance(item, dict):
raise ValueError raise ValueError
_validate_variable_keys(item)
all_vars = combine_vars(all_vars, item) all_vars = combine_vars(all_vars, item)
return all_vars return all_vars
elif ds is None: elif ds is None:
@ -395,6 +402,8 @@ class Base:
raise ValueError raise ValueError
except ValueError: except ValueError:
raise AnsibleParserError("Vars in a %s must be specified as a dictionary, or a list of dictionaries" % self.__class__.__name__, obj=ds) raise AnsibleParserError("Vars in a %s must be specified as a dictionary, or a list of dictionaries" % self.__class__.__name__, obj=ds)
except TypeError, e:
raise AnsibleParserError("Invalid variable name in vars specified for %s: %s" % (self.__class__.__name__, e), obj=ds)
def _extend_value(self, value, new_value): def _extend_value(self, value, new_value):
''' '''

View file

@ -14,47 +14,16 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import ast
from six import string_types
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.utils.boolean import boolean from ansible.utils.boolean import boolean
from ansible.utils.vars import isidentifier
def isidentifier(ident):
"""
Determines, if string is valid Python identifier using the ast module.
Orignally posted at: http://stackoverflow.com/a/29586366
"""
if not isinstance(ident, string_types):
return False
try:
root = ast.parse(ident)
except SyntaxError:
return False
if not isinstance(root, ast.Module):
return False
if len(root.body) != 1:
return False
if not isinstance(root.body[0], ast.Expr):
return False
if not isinstance(root.body[0].value, ast.Name):
return False
if root.body[0].value.id != ident:
return False
return True
class ActionModule(ActionBase): class ActionModule(ActionBase):

View file

@ -19,6 +19,9 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import ast
from six import string_types
from ansible import constants as C from ansible import constants as C
from ansible.parsing.splitter import parse_kv from ansible.parsing.splitter import parse_kv
@ -66,3 +69,35 @@ def load_extra_vars(loader, options):
data = parse_kv(extra_vars_opt) data = parse_kv(extra_vars_opt)
extra_vars = combine_vars(extra_vars, data) extra_vars = combine_vars(extra_vars, data)
return extra_vars return extra_vars
def isidentifier(ident):
"""
Determines, if string is valid Python identifier using the ast module.
Orignally posted at: http://stackoverflow.com/a/29586366
"""
if not isinstance(ident, string_types):
return False
try:
root = ast.parse(ident)
except SyntaxError:
return False
if not isinstance(root, ast.Module):
return False
if len(root.body) != 1:
return False
if not isinstance(root.body[0], ast.Expr):
return False
if not isinstance(root.body[0].value, ast.Name):
return False
if root.body[0].value.id != ident:
return False
return True