diff --git a/changelogs/fragments/remove-safe-eval.yml b/changelogs/fragments/remove-safe-eval.yml new file mode 100644 index 00000000000..152249dcd5e --- /dev/null +++ b/changelogs/fragments/remove-safe-eval.yml @@ -0,0 +1,3 @@ +--- +removed_features: + - module_utils - Remove previously deprecated ``safe_eval`` function (#85996) (#85999) diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index a630e4bf026..e67299b8611 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -161,7 +161,6 @@ from ansible.module_utils.common.parameters import ( from ansible.module_utils.errors import AnsibleFallbackNotFound, AnsibleValidationErrorMultiple, UnsupportedError from ansible.module_utils.common.validation import ( check_missing_parameters, - safe_eval, ) from ansible.module_utils.common._utils import get_all_subclasses as _get_all_subclasses from ansible.module_utils.parsing.convert_bool import BOOLEANS, BOOLEANS_FALSE, BOOLEANS_TRUE, boolean @@ -1244,10 +1243,6 @@ class AnsibleModule(object): if not hasattr(self, PASS_VARS[k][0]): setattr(self, PASS_VARS[k][0], PASS_VARS[k][1]) - def safe_eval(self, value, locals=None, include_exceptions=False): - # deprecated: description='no longer used in the codebase' core_version='2.21' - return safe_eval(value, locals, include_exceptions) - def _load_params(self): """ read the input and set the params attribute. diff --git a/lib/ansible/module_utils/common/validation.py b/lib/ansible/module_utils/common/validation.py index 81100cd5bce..76028cc5ba7 100644 --- a/lib/ansible/module_utils/common/validation.py +++ b/lib/ansible/module_utils/common/validation.py @@ -7,7 +7,6 @@ from __future__ import annotations import decimal import json import os -import re from ast import literal_eval from ansible.module_utils._internal import _no_six @@ -15,7 +14,6 @@ from ansible.module_utils.common import json as _common_json from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.collections import is_iterable from ansible.module_utils.common.text.formatters import human_to_bytes -from ansible.module_utils.common.warnings import deprecate from ansible.module_utils.parsing.convert_bool import boolean @@ -35,38 +33,6 @@ def count_terms(terms, parameters): return len(set(terms).intersection(parameters)) -def safe_eval(value, locals=None, include_exceptions=False): - deprecate( - "The safe_eval function should not be used.", - version="2.21", - ) - # do not allow method calls to modules - if not isinstance(value, str): - # already templated to a datavaluestructure, perhaps? - if include_exceptions: - return (value, None) - return value - if re.search(r'\w\.\w+\(', value): - if include_exceptions: - return (value, None) - return value - # do not allow imports - if re.search(r'import \w+', value): - if include_exceptions: - return (value, None) - return value - try: - result = literal_eval(value) - if include_exceptions: - return (result, None) - else: - return result - except Exception as e: - if include_exceptions: - return (value, e) - return value - - def check_mutually_exclusive(terms, parameters, options_context=None): """Check mutually exclusive terms against argument parameters diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index e5081746dc9..bb3f024e21f 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -234,8 +234,6 @@ test/integration/targets/ansible-test-sanity-pylint/deprecated_thing.py pylint:a test/integration/targets/ansible-test-sanity-pylint/deprecated_thing.py pylint:ansible-deprecated-unnecessary-collection-name # required to verify plugin against core test/integration/targets/ansible-test-sanity-pylint/deprecated_thing.py pylint:ansible-deprecated-collection-name-not-permitted # required to verify plugin against core lib/ansible/module_utils/basic.py pylint:ansible-deprecated-version # TODO: 2.21 -lib/ansible/module_utils/basic.py pylint:ansible-deprecated-version-comment # TODO: 2.21 -lib/ansible/module_utils/common/validation.py pylint:ansible-deprecated-version # TODO: 2.21 lib/ansible/module_utils/compat/paramiko.py pylint:ansible-deprecated-version # TODO: 2.21 lib/ansible/plugins/connection/paramiko_ssh.py pylint:ansible-deprecated-version # TODO: 2.21 lib/ansible/plugins/strategy/__init__.py pylint:ansible-deprecated-version # TODO: 2.21 diff --git a/test/units/module_utils/basic/test_safe_eval.py b/test/units/module_utils/basic/test_safe_eval.py deleted file mode 100644 index 3df214c3562..00000000000 --- a/test/units/module_utils/basic/test_safe_eval.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -# (c) 2015-2017, Toshio Kuratomi -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import annotations - -from itertools import chain - -import typing as t - -import pytest - - -# Strings that should be converted into a typed value -VALID_STRINGS: tuple[tuple[str, t.Any], ...] = ( - ("'a'", 'a'), - ("'1'", '1'), - ("1", 1), - ("True", True), - ("False", False), - ("{}", {}), -) - -# Passing things that aren't strings should just return the object -NONSTRINGS = ( - ({'a': 1}, {'a': 1}), -) - -# These strings are not basic types. For security, these should not be -# executed. We return the same string and get an exception for some -INVALID_STRINGS = ( - ("a=1", "a=1", SyntaxError), - ("a.foo()", "a.foo()", None), - ("import foo", "import foo", None), - ("__import__('foo')", "__import__('foo')", ValueError), -) - - -@pytest.mark.parametrize('code, expected, stdin', - ((c, e, {}) for c, e in chain(VALID_STRINGS, NONSTRINGS)), - indirect=['stdin']) -def test_simple_types(am, code, expected): - # test some basic usage for various types - assert am.safe_eval(code) == expected - - -@pytest.mark.parametrize('code, expected, stdin', - ((c, e, {}) for c, e in chain(VALID_STRINGS, NONSTRINGS)), - indirect=['stdin']) -def test_simple_types_with_exceptions(am, code, expected): - # Test simple types with exceptions requested - assert am.safe_eval(code, include_exceptions=True), (expected, None) - - -@pytest.mark.parametrize('code, expected, stdin', - ((c, e, {}) for c, e, dummy in INVALID_STRINGS), - indirect=['stdin']) -def test_invalid_strings(am, code, expected): - assert am.safe_eval(code) == expected - - -@pytest.mark.parametrize('code, expected, exception, stdin', - ((c, e, ex, {}) for c, e, ex in INVALID_STRINGS), - indirect=['stdin']) -def test_invalid_strings_with_exceptions(am, code, expected, exception): - res = am.safe_eval(code, include_exceptions=True) - assert res[0] == expected - if exception is None: - assert res[1] == exception - else: - assert isinstance(res[1], exception)