From d250bb5217b200ea263db97f3b0b918e3e75c574 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Mon, 25 Mar 2024 06:50:17 +0100 Subject: [PATCH] [stable-6] inventory plugins: make data obtained from remote unsafe (#8147) inventory plugins: make data obtained from remote unsafe (#8098) Make data obtained from remote unsafe. (cherry picked from commit d62fe154d296f7d738dd4dad0263bd918aa4e607) --- changelogs/fragments/inventory-rce.yml | 6 ++++++ plugins/inventory/cobbler.py | 7 ++++--- plugins/inventory/gitlab_runners.py | 7 ++++--- plugins/inventory/icinga2.py | 11 +++++----- plugins/inventory/linode.py | 21 +++++++++++-------- plugins/inventory/lxd.py | 29 +++++++++++++++++--------- plugins/inventory/nmap.py | 2 ++ plugins/inventory/online.py | 17 +++++++++------ plugins/inventory/opennebula.py | 2 ++ plugins/inventory/proxmox.py | 3 ++- plugins/inventory/scaleway.py | 5 +++-- plugins/inventory/stackpath_compute.py | 3 ++- plugins/inventory/virtualbox.py | 13 +++++++----- plugins/inventory/xen_orchestra.py | 3 ++- 14 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 changelogs/fragments/inventory-rce.yml diff --git a/changelogs/fragments/inventory-rce.yml b/changelogs/fragments/inventory-rce.yml new file mode 100644 index 0000000000..9eee6dff52 --- /dev/null +++ b/changelogs/fragments/inventory-rce.yml @@ -0,0 +1,6 @@ +security_fixes: + - "cobbler, gitlab_runners, icinga2, linode, lxd, nmap, online, opennebula, proxmox, scaleway, stackpath_compute, virtualbox, + and xen_orchestra inventory plugin - make sure all data received from the remote servers is marked as unsafe, so remote + code execution by obtaining texts that can be evaluated as templates is not possible + (https://www.die-welt.net/2024/03/remote-code-execution-in-ansible-dynamic-inventory-plugins/, + https://github.com/ansible-collections/community.general/pull/8098)." diff --git a/plugins/inventory/cobbler.py b/plugins/inventory/cobbler.py index 80c144d297..cfa77eaead 100644 --- a/plugins/inventory/cobbler.py +++ b/plugins/inventory/cobbler.py @@ -88,6 +88,7 @@ from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.six import iteritems from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, to_safe_group_name from ansible.module_utils.six import text_type +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe # xmlrpc try: @@ -239,7 +240,7 @@ class InventoryModule(BaseInventoryPlugin, Cacheable): for host in self._get_systems(): # Get the FQDN for the host and add it to the right groups - hostname = host['hostname'] # None + hostname = make_unsafe(host['hostname']) # None interfaces = host['interfaces'] if self._exclude_profile(host['profile']): @@ -252,7 +253,7 @@ class InventoryModule(BaseInventoryPlugin, Cacheable): if ivalue['management'] or not ivalue['static']: this_dns_name = ivalue.get('dns_name', None) if this_dns_name is not None and this_dns_name != "": - hostname = this_dns_name + hostname = make_unsafe(this_dns_name) self.display.vvvv('Set hostname to %s from %s\n' % (hostname, iname)) if hostname == '': @@ -283,6 +284,6 @@ class InventoryModule(BaseInventoryPlugin, Cacheable): # Add host variables if self.get_option('want_facts'): try: - self.inventory.set_variable(hostname, 'cobbler', host) + self.inventory.set_variable(hostname, 'cobbler', make_unsafe(host)) except ValueError as e: self.display.warning("Could not set host info for %s: %s" % (hostname, to_text(e))) diff --git a/plugins/inventory/gitlab_runners.py b/plugins/inventory/gitlab_runners.py index d68b8d4e28..e6fcdbc5a1 100644 --- a/plugins/inventory/gitlab_runners.py +++ b/plugins/inventory/gitlab_runners.py @@ -84,6 +84,7 @@ keyed_groups: from ansible.errors import AnsibleError, AnsibleParserError from ansible.module_utils.common.text.converters import to_native from ansible.plugins.inventory import BaseInventoryPlugin, Constructable +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe try: import gitlab @@ -106,11 +107,11 @@ class InventoryModule(BaseInventoryPlugin, Constructable): else: runners = gl.runners.all() for runner in runners: - host = str(runner['id']) + host = make_unsafe(str(runner['id'])) ip_address = runner['ip_address'] - host_attrs = vars(gl.runners.get(runner['id']))['_attrs'] + host_attrs = make_unsafe(vars(gl.runners.get(runner['id']))['_attrs']) self.inventory.add_host(host, group='gitlab_runners') - self.inventory.set_variable(host, 'ansible_host', ip_address) + self.inventory.set_variable(host, 'ansible_host', make_unsafe(ip_address)) if self.get_option('verbose_output', True): self.inventory.set_variable(host, 'gitlab_runner_attributes', host_attrs) diff --git a/plugins/inventory/icinga2.py b/plugins/inventory/icinga2.py index 70e0f57332..29bee907b2 100644 --- a/plugins/inventory/icinga2.py +++ b/plugins/inventory/icinga2.py @@ -96,6 +96,7 @@ from ansible.errors import AnsibleParserError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable from ansible.module_utils.urls import open_url from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe class InventoryModule(BaseInventoryPlugin, Constructable): @@ -233,15 +234,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable): """Convert Icinga2 API data to JSON format for Ansible""" groups_dict = {"_meta": {"hostvars": {}}} for entry in json_data: - host_attrs = entry['attrs'] + host_attrs = make_unsafe(entry['attrs']) if self.inventory_attr == "name": - host_name = entry.get('name') + host_name = make_unsafe(entry.get('name')) if self.inventory_attr == "address": # When looking for address for inventory, if missing fallback to object name if host_attrs.get('address', '') != '': - host_name = host_attrs.get('address') + host_name = make_unsafe(host_attrs.get('address')) else: - host_name = entry.get('name') + host_name = make_unsafe(entry.get('name')) if self.inventory_attr == "display_name": host_name = host_attrs.get('display_name') if host_attrs['state'] == 0: @@ -257,7 +258,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable): # If the address attribute is populated, override ansible_host with the value if host_attrs.get('address') != '': self.inventory.set_variable(host_name, 'ansible_host', host_attrs.get('address')) - self.inventory.set_variable(host_name, 'hostname', entry.get('name')) + self.inventory.set_variable(host_name, 'hostname', make_unsafe(entry.get('name'))) self.inventory.set_variable(host_name, 'display_name', host_attrs.get('display_name')) self.inventory.set_variable(host_name, 'state', host_attrs['state']) diff --git a/plugins/inventory/linode.py b/plugins/inventory/linode.py index b28cfa27ba..46c9ddc761 100644 --- a/plugins/inventory/linode.py +++ b/plugins/inventory/linode.py @@ -123,6 +123,7 @@ compose: from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe try: @@ -199,20 +200,21 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _add_instances_to_groups(self): """Add instance names to their dynamic inventory groups.""" for instance in self.instances: - self.inventory.add_host(instance.label, group=instance.group) + self.inventory.add_host(make_unsafe(instance.label), group=instance.group) def _add_hostvars_for_instances(self): """Add hostvars for instances in the dynamic inventory.""" ip_style = self.get_option('ip_style') for instance in self.instances: hostvars = instance._raw_json + hostname = make_unsafe(instance.label) for hostvar_key in hostvars: if ip_style == 'api' and hostvar_key in ['ipv4', 'ipv6']: continue self.inventory.set_variable( - instance.label, + hostname, hostvar_key, - hostvars[hostvar_key] + make_unsafe(hostvars[hostvar_key]) ) if ip_style == 'api': ips = instance.ips.ipv4.public + instance.ips.ipv4.private @@ -221,9 +223,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): for ip_type in set(ip.type for ip in ips): self.inventory.set_variable( - instance.label, + hostname, ip_type, - self._ip_data([ip for ip in ips if ip.type == ip_type]) + make_unsafe(self._ip_data([ip for ip in ips if ip.type == ip_type])) ) def _ip_data(self, ip_list): @@ -254,21 +256,22 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self._add_instances_to_groups() self._add_hostvars_for_instances() for instance in self.instances: - variables = self.inventory.get_host(instance.label).get_vars() + hostname = make_unsafe(instance.label) + variables = self.inventory.get_host(hostname).get_vars() self._add_host_to_composed_groups( self.get_option('groups'), variables, - instance.label, + hostname, strict=strict) self._add_host_to_keyed_groups( self.get_option('keyed_groups'), variables, - instance.label, + hostname, strict=strict) self._set_composite_vars( self.get_option('compose'), variables, - instance.label, + hostname, strict=strict) def verify_file(self, path): diff --git a/plugins/inventory/lxd.py b/plugins/inventory/lxd.py index bd0a6ce008..3ad2fb01d2 100644 --- a/plugins/inventory/lxd.py +++ b/plugins/inventory/lxd.py @@ -161,6 +161,7 @@ from ansible.module_utils.six import raise_from from ansible.errors import AnsibleError, AnsibleParserError from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe try: import ipaddress @@ -656,7 +657,7 @@ class InventoryModule(BaseInventoryPlugin): if self._get_data_entry('inventory/{0}/network_interfaces'.format(instance_name)): # instance have network interfaces self.inventory.set_variable(instance_name, 'ansible_connection', 'ssh') - self.inventory.set_variable(instance_name, 'ansible_host', interface_selection(instance_name)) + self.inventory.set_variable(instance_name, 'ansible_host', make_unsafe(interface_selection(instance_name))) else: self.inventory.set_variable(instance_name, 'ansible_connection', 'local') @@ -682,31 +683,39 @@ class InventoryModule(BaseInventoryPlugin): if self.filter.lower() != instance_state: continue # add instance + instance_name = make_unsafe(instance_name) self.inventory.add_host(instance_name) # add network informations self.build_inventory_network(instance_name) # add os v = self._get_data_entry('inventory/{0}/os'.format(instance_name)) if v: - self.inventory.set_variable(instance_name, 'ansible_lxd_os', v.lower()) + self.inventory.set_variable(instance_name, 'ansible_lxd_os', make_unsafe(v.lower())) # add release v = self._get_data_entry('inventory/{0}/release'.format(instance_name)) if v: - self.inventory.set_variable(instance_name, 'ansible_lxd_release', v.lower()) + self.inventory.set_variable( + instance_name, 'ansible_lxd_release', make_unsafe(v.lower())) # add profile - self.inventory.set_variable(instance_name, 'ansible_lxd_profile', self._get_data_entry('inventory/{0}/profile'.format(instance_name))) + self.inventory.set_variable( + instance_name, 'ansible_lxd_profile', make_unsafe(self._get_data_entry('inventory/{0}/profile'.format(instance_name)))) # add state - self.inventory.set_variable(instance_name, 'ansible_lxd_state', instance_state) + self.inventory.set_variable( + instance_name, 'ansible_lxd_state', make_unsafe(instance_state)) # add type - self.inventory.set_variable(instance_name, 'ansible_lxd_type', self._get_data_entry('inventory/{0}/type'.format(instance_name))) + self.inventory.set_variable( + instance_name, 'ansible_lxd_type', make_unsafe(self._get_data_entry('inventory/{0}/type'.format(instance_name)))) # add location information if self._get_data_entry('inventory/{0}/location'.format(instance_name)) != "none": # wrong type by lxd 'none' != 'None' - self.inventory.set_variable(instance_name, 'ansible_lxd_location', self._get_data_entry('inventory/{0}/location'.format(instance_name))) + self.inventory.set_variable( + instance_name, 'ansible_lxd_location', make_unsafe(self._get_data_entry('inventory/{0}/location'.format(instance_name)))) # add VLAN_ID information if self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name)): - self.inventory.set_variable(instance_name, 'ansible_lxd_vlan_ids', self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name))) + self.inventory.set_variable( + instance_name, 'ansible_lxd_vlan_ids', make_unsafe(self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name)))) # add project - self.inventory.set_variable(instance_name, 'ansible_lxd_project', self._get_data_entry('inventory/{0}/project'.format(instance_name))) + self.inventory.set_variable( + instance_name, 'ansible_lxd_project', make_unsafe(self._get_data_entry('inventory/{0}/project'.format(instance_name)))) def build_inventory_groups_location(self, group_name): """create group by attribute: location @@ -979,7 +988,7 @@ class InventoryModule(BaseInventoryPlugin): for group_name in self.groupby: if not group_name.isalnum(): raise AnsibleParserError('Invalid character(s) in groupname: {0}'.format(to_native(group_name))) - group_type(group_name) + group_type(make_unsafe(group_name)) def build_inventory(self): """Build dynamic inventory diff --git a/plugins/inventory/nmap.py b/plugins/inventory/nmap.py index a03cf3e6fa..10ae7631b3 100644 --- a/plugins/inventory/nmap.py +++ b/plugins/inventory/nmap.py @@ -121,6 +121,7 @@ from ansible.errors import AnsibleParserError from ansible.module_utils.common.text.converters import to_native, to_text from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable from ansible.module_utils.common.process import get_bin_path +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): @@ -138,6 +139,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): strict = self.get_option('strict') for host in hosts: + host = make_unsafe(host) hostname = host['name'] self.inventory.add_host(hostname) for var, value in host.items(): diff --git a/plugins/inventory/online.py b/plugins/inventory/online.py index 3fccd58d2f..b3a9ecd379 100644 --- a/plugins/inventory/online.py +++ b/plugins/inventory/online.py @@ -68,6 +68,7 @@ from ansible.plugins.inventory import BaseInventoryPlugin from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.ansible_release import __version__ as ansible_version from ansible.module_utils.six.moves.urllib.parse import urljoin +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe class InventoryModule(BaseInventoryPlugin): @@ -169,20 +170,20 @@ class InventoryModule(BaseInventoryPlugin): "support" ) for attribute in targeted_attributes: - self.inventory.set_variable(hostname, attribute, host_infos[attribute]) + self.inventory.set_variable(hostname, attribute, make_unsafe(host_infos[attribute])) if self.extract_public_ipv4(host_infos=host_infos): - self.inventory.set_variable(hostname, "public_ipv4", self.extract_public_ipv4(host_infos=host_infos)) - self.inventory.set_variable(hostname, "ansible_host", self.extract_public_ipv4(host_infos=host_infos)) + self.inventory.set_variable(hostname, "public_ipv4", make_unsafe(self.extract_public_ipv4(host_infos=host_infos))) + self.inventory.set_variable(hostname, "ansible_host", make_unsafe(self.extract_public_ipv4(host_infos=host_infos))) if self.extract_private_ipv4(host_infos=host_infos): - self.inventory.set_variable(hostname, "public_ipv4", self.extract_private_ipv4(host_infos=host_infos)) + self.inventory.set_variable(hostname, "public_ipv4", make_unsafe(self.extract_private_ipv4(host_infos=host_infos))) if self.extract_os_name(host_infos=host_infos): - self.inventory.set_variable(hostname, "os_name", self.extract_os_name(host_infos=host_infos)) + self.inventory.set_variable(hostname, "os_name", make_unsafe(self.extract_os_name(host_infos=host_infos))) if self.extract_os_version(host_infos=host_infos): - self.inventory.set_variable(hostname, "os_version", self.extract_os_name(host_infos=host_infos)) + self.inventory.set_variable(hostname, "os_version", make_unsafe(self.extract_os_name(host_infos=host_infos))) def _filter_host(self, host_infos, hostname_preferences): @@ -201,6 +202,8 @@ class InventoryModule(BaseInventoryPlugin): if not hostname: return + hostname = make_unsafe(hostname) + self.inventory.add_host(host=hostname) self._fill_host_variables(hostname=hostname, host_infos=host_infos) @@ -210,6 +213,8 @@ class InventoryModule(BaseInventoryPlugin): if not group: return + group = make_unsafe(group) + self.inventory.add_group(group=group) self.inventory.add_host(group=group, host=hostname) diff --git a/plugins/inventory/opennebula.py b/plugins/inventory/opennebula.py index 603920edc2..83fd759f96 100644 --- a/plugins/inventory/opennebula.py +++ b/plugins/inventory/opennebula.py @@ -97,6 +97,7 @@ except ImportError: from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable from ansible.module_utils.common.text.converters import to_native +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe from collections import namedtuple import os @@ -215,6 +216,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable): filter_by_label = self.get_option('filter_by_label') servers = self._retrieve_servers(filter_by_label) for server in servers: + server = make_unsafe(server) hostname = server['name'] # check for labels if group_by_labels and server['LABELS']: diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index dc2e1febca..39ace24764 100644 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -222,6 +222,7 @@ from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.six import string_types from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible.utils.display import Display +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe from ansible_collections.community.general.plugins.module_utils.version import LooseVersion @@ -330,7 +331,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self._cache[self.cache_key][url] = data - return self._cache[self.cache_key][url] + return make_unsafe(self._cache[self.cache_key][url]) def _get_nodes(self): return self._get_json("%s/api2/json/nodes" % self.proxmox_url) diff --git a/plugins/inventory/scaleway.py b/plugins/inventory/scaleway.py index 6aacc9f665..ea4fb26ca7 100644 --- a/plugins/inventory/scaleway.py +++ b/plugins/inventory/scaleway.py @@ -124,6 +124,7 @@ from ansible_collections.community.general.plugins.module_utils.scaleway import from ansible.module_utils.urls import open_url from ansible.module_utils.common.text.converters import to_native, to_text from ansible.module_utils.six import raise_from +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe import ansible.module_utils.six.moves.urllib.parse as urllib_parse @@ -279,7 +280,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable): zone_info = SCALEWAY_LOCATION[zone] url = _build_server_url(zone_info["api_endpoint"]) - raw_zone_hosts_infos = _fetch_information(url=url, token=token) + raw_zone_hosts_infos = make_unsafe(_fetch_information(url=url, token=token)) for host_infos in raw_zone_hosts_infos: @@ -341,4 +342,4 @@ class InventoryModule(BaseInventoryPlugin, Constructable): hostname_preference = self.get_option("hostnames") for zone in self._get_zones(config_zones): - self.do_zone_inventory(zone=zone, token=token, tags=tags, hostname_preferences=hostname_preference) + self.do_zone_inventory(zone=make_unsafe(zone), token=token, tags=tags, hostname_preferences=hostname_preference) diff --git a/plugins/inventory/stackpath_compute.py b/plugins/inventory/stackpath_compute.py index 39f880e820..9a556d39e0 100644 --- a/plugins/inventory/stackpath_compute.py +++ b/plugins/inventory/stackpath_compute.py @@ -72,6 +72,7 @@ from ansible.plugins.inventory import ( Cacheable ) from ansible.utils.display import Display +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe display = Display() @@ -271,7 +272,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): if not cache or cache_needs_update: results = self._query() - self._populate(results) + self._populate(make_unsafe(results)) # If the cache has expired/doesn't exist or # if refresh_inventory/flush cache is used diff --git a/plugins/inventory/virtualbox.py b/plugins/inventory/virtualbox.py index c926d8b449..8604808e15 100644 --- a/plugins/inventory/virtualbox.py +++ b/plugins/inventory/virtualbox.py @@ -62,6 +62,7 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_ from ansible.module_utils.common._collections_compat import MutableMapping from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable from ansible.module_utils.common.process import get_bin_path +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): @@ -116,6 +117,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self._add_host_to_keyed_groups(self.get_option('keyed_groups'), hostvars[host], host, strict=strict) def _populate_from_cache(self, source_data): + source_data = make_unsafe(source_data) hostvars = source_data.pop('_meta', {}).get('hostvars', {}) for group in source_data: if group == 'all': @@ -162,7 +164,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): v = v.strip() # found host if k.startswith('Name') and ',' not in v: # some setting strings appear in Name - current_host = v + current_host = make_unsafe(v) if current_host not in hostvars: hostvars[current_host] = {} self.inventory.add_host(current_host) @@ -170,12 +172,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): # try to get network info netdata = self._query_vbox_data(current_host, netinfo) if netdata: - self.inventory.set_variable(current_host, 'ansible_host', netdata) + self.inventory.set_variable(current_host, 'ansible_host', make_unsafe(netdata)) # found groups elif k == 'Groups': for group in v.split('/'): if group: + group = make_unsafe(group) group = self.inventory.add_group(group) self.inventory.add_child(group, current_host) if group not in cacheable_results: @@ -185,17 +188,17 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): else: # found vars, accumulate in hostvars for clean inventory set - pref_k = 'vbox_' + k.strip().replace(' ', '_') + pref_k = make_unsafe('vbox_' + k.strip().replace(' ', '_')) leading_spaces = len(k) - len(k.lstrip(' ')) if 0 < leading_spaces <= 2: if prevkey not in hostvars[current_host] or not isinstance(hostvars[current_host][prevkey], dict): hostvars[current_host][prevkey] = {} - hostvars[current_host][prevkey][pref_k] = v + hostvars[current_host][prevkey][pref_k] = make_unsafe(v) elif leading_spaces > 2: continue else: if v != '': - hostvars[current_host][pref_k] = v + hostvars[current_host][pref_k] = make_unsafe(v) if self._ungrouped_host(current_host, cacheable_results): if 'ungrouped' not in cacheable_results: cacheable_results['ungrouped'] = {'hosts': []} diff --git a/plugins/inventory/xen_orchestra.py b/plugins/inventory/xen_orchestra.py index ddbdd9bb04..48750e1769 100644 --- a/plugins/inventory/xen_orchestra.py +++ b/plugins/inventory/xen_orchestra.py @@ -82,6 +82,7 @@ from time import sleep from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable +from ansible.utils.unsafe_proxy import wrap_var as make_unsafe from ansible_collections.community.general.plugins.module_utils.version import LooseVersion @@ -347,4 +348,4 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self.protocol = 'ws' objects = self._get_objects() - self._populate(objects) + self._populate(make_unsafe(objects))