From 204fc7becfb0426b5bc9334971c75bf9ca3c1581 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Thu, 7 Jun 2018 07:41:20 -0700 Subject: [PATCH] Make config files obey the utf-8 input rule When we read files from disk we always want to read them as bytes and then convert them to text ourselves. This gives us greater control over what encodings are used, what to do in case of errors decoding the bytes to text, and better resilience against problems on both Python 2 and Python 3. If we left it up to Python to do this, on Python2, this could mean that config values end up as bytes (leading to tracebacks elsewhere in the code). In Python3, it could traceback if the user's locale did not match with the encoding of the ini file or config files could be decoded as the user's locale encoding instead of as utf-8. --- lib/ansible/config/manager.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py index 36f8a6a3c51..25c091a5ce8 100644 --- a/lib/ansible/config/manager.py +++ b/lib/ansible/config/manager.py @@ -8,6 +8,7 @@ import os import sys import tempfile +import io from collections import namedtuple from yaml import load as yaml_load @@ -19,7 +20,7 @@ except ImportError: from ansible.config.data import ConfigData from ansible.errors import AnsibleOptionsError, AnsibleError -from ansible.module_utils.six import string_types +from ansible.module_utils.six import PY3, string_types from ansible.module_utils.six.moves import configparser from ansible.module_utils._text import to_text, to_bytes, to_native from ansible.module_utils.parsing.convert_bool import boolean @@ -215,8 +216,17 @@ class ConfigManager(object): if cfile is not None: if ftype == 'ini': self._parsers[cfile] = configparser.ConfigParser() + with open(cfile, 'rb') as f: + try: + cfg_text = to_text(f.read(), errors='surrogate_or_strict') + except UnicodeError: + raise AnsibleOptionsError("Error reading config file(%s) because the config file was not utf8 encoded: %s" % (cfile, to_native(e))) try: - self._parsers[cfile].read(cfile) + if PY3: + self._parsers[cfile].read_string(cfg_text) + else: + cfg_file = io.StringIO(cfg_text) + self._parsers[cfile].readfp(cfg_file) except configparser.Error as e: raise AnsibleOptionsError("Error reading config file (%s): %s" % (cfile, to_native(e))) # FIXME: this should eventually handle yaml config files