diff --git a/ddtrace/appsec/_iast/_ast/ast_patching.py b/ddtrace/appsec/_iast/_ast/ast_patching.py index bb40650850b..22f74daed6c 100644 --- a/ddtrace/appsec/_iast/_ast/ast_patching.py +++ b/ddtrace/appsec/_iast/_ast/ast_patching.py @@ -7,7 +7,6 @@ from sys import builtin_module_names from types import ModuleType from typing import Optional -from typing import Set from typing import Text from typing import Tuple @@ -25,19 +24,58 @@ # Prefixes for modules where IAST patching is allowed IAST_ALLOWLIST: Tuple[Text, ...] = ("tests.appsec.iast",) IAST_DENYLIST: Tuple[Text, ...] = ( + "flask", + "werkzeug", + "crypto", # This module is patched by the IAST patch methods, propagation is not needed + "deprecated", + "api_pb2", # Patching crashes with these auto-generated modules, propagation is not needed + "api_pb2_grpc", # ditto + "asyncpg.pgproto", + "blinker", + "bytecode", + "cattrs", + "click", + "ddsketch", "ddtrace", - "pkg_resources", "encodings", # this package is used to load encodings when a module is imported, propagation is not needed + "envier", + "exceptiongroup", + "freezegun", # Testing utilities for time manipulation + "hypothesis", + "importlib_metadata", "inspect", # this package is used to get the stack frames, propagation is not needed + "itsdangerous", + "opentelemetry-api", + "packaging", + "pip", + "pkg_resources", + "pluggy", + "protobuf", "pycparser", # this package is called when a module is imported, propagation is not needed - "Crypto", # This module is patched by the IAST patch methods, propagation is not needed - "api_pb2", # Patching crashes with these auto-generated modules, propagation is not needed - "api_pb2_grpc", # ditto - "unittest.mock", "pytest", # Testing framework - "freezegun", # Testing utilities for time manipulation + "setuptools", "sklearn", # Machine learning library + "tomli", + "typing_extensions", + "unittest.mock", + "uvloop", "urlpatterns_reverse.tests", # assertRaises eat exceptions in native code, so we don't call the original function + "wrapt", + "zipp", + ## This is a workaround for Sanic failures: + "websocket", + "h11", + "aioquic", + "httptools", + "sniffio", + "py", + "sanic", + "rich", + "httpx", + "websockets", + "uvicorn", + "anyio", + "httpcore", ) @@ -67,24 +105,10 @@ def get_encoding(module_path: Text) -> Text: return ENCODING -try: - import importlib.metadata as il_md -except ImportError: - import importlib_metadata as il_md # type: ignore[no-redef] - - -def _build_installed_package_names_list() -> Set[Text]: - return { - ilmd_d.metadata["name"] for ilmd_d in il_md.distributions() if ilmd_d is not None and ilmd_d.files is not None - } - - -_NOT_PATCH_MODULE_NAMES = ( - _build_installed_package_names_list() | _stdlib_for_python_version() | set(builtin_module_names) -) +_NOT_PATCH_MODULE_NAMES = _stdlib_for_python_version() | set(builtin_module_names) -def _in_python_stdlib_or_third_party(module_name: str) -> bool: +def _in_python_stdlib(module_name: str) -> bool: return module_name.split(".")[0].lower() in [x.lower() for x in _NOT_PATCH_MODULE_NAMES] @@ -98,11 +122,11 @@ def _should_iast_patch(module_name: Text) -> bool: # max_deny = max((len(prefix) for prefix in IAST_DENYLIST if module_name.startswith(prefix)), default=-1) # diff = max_allow - max_deny # return diff > 0 or (diff == 0 and not _in_python_stdlib_or_third_party(module_name)) - if module_name.startswith(IAST_ALLOWLIST): + if module_name.lower().startswith(IAST_ALLOWLIST): return True - if module_name.startswith(IAST_DENYLIST): + if module_name.lower().startswith(IAST_DENYLIST): return False - return not _in_python_stdlib_or_third_party(module_name) + return not _in_python_stdlib(module_name) def visit_ast( diff --git a/tests/appsec/iast/_ast/test_ast_patching.py b/tests/appsec/iast/_ast/test_ast_patching.py index bb075665848..e0fd4960c72 100644 --- a/tests/appsec/iast/_ast/test_ast_patching.py +++ b/tests/appsec/iast/_ast/test_ast_patching.py @@ -5,7 +5,7 @@ import mock import pytest -from ddtrace.appsec._iast._ast.ast_patching import _in_python_stdlib_or_third_party +from ddtrace.appsec._iast._ast.ast_patching import _in_python_stdlib from ddtrace.appsec._iast._ast.ast_patching import _should_iast_patch from ddtrace.appsec._iast._ast.ast_patching import astpatch_module from ddtrace.appsec._iast._ast.ast_patching import visit_ast @@ -136,19 +136,19 @@ def test_module_should_iast_patch(): @pytest.mark.parametrize( "module_name, result", [ - ("Envier", True), + ("Envier", False), ("iterTools", True), ("functooLs", True), - ("astunparse", True), - ("pytest.warns", True), + ("astunparse", False), + ("pytest.warns", False), ("datetime", True), ("posiX", True), ("app", False), ("my_app", False), ], ) -def test_module_in_python_stdlib_or_third_party(module_name, result): - assert _in_python_stdlib_or_third_party(module_name) == result +def test_module_in_python_stdlib(module_name, result): + assert _in_python_stdlib(module_name) == result def test_module_path_none(caplog): diff --git a/tests/appsec/iast_packages/packages/pkg_iniconfig.py b/tests/appsec/iast_packages/packages/pkg_iniconfig.py index 3318bd449be..4f204d7ee54 100644 --- a/tests/appsec/iast_packages/packages/pkg_iniconfig.py +++ b/tests/appsec/iast_packages/packages/pkg_iniconfig.py @@ -67,18 +67,13 @@ def pkg_iniconfig_propagation_view(): ini_path = "example.ini" try: - with open(ini_path, "w") as f: - f.write(ini_content) - - config = iniconfig.IniConfig(ini_path) - parsed_data = {section.name: list(section.items()) for section in config} - value = parsed_data["section"][0][1] + config = iniconfig.IniConfig(ini_path, data=ini_content) + read_value = config["section"]["key"] result_output = ( - "OK" if is_pyobject_tainted(value) else f"Error: value from parsed_data is not tainted: {value}" + "OK" + if is_pyobject_tainted(read_value) + else f"Error: read_value from parsed_data is not tainted: {read_value}" ) - - if os.path.exists(ini_path): - os.remove(ini_path) except Exception as e: result_output = f"Error: {str(e)}" except Exception as e: diff --git a/tests/appsec/iast_packages/test_packages.py b/tests/appsec/iast_packages/test_packages.py index d79234de698..ade6404bc9e 100644 --- a/tests/appsec/iast_packages/test_packages.py +++ b/tests/appsec/iast_packages/test_packages.py @@ -399,7 +399,6 @@ def uninstall(self, python_cmd): "", import_module_to_validate="platformdirs.unix", test_propagation=True, - fixme_propagation_fails=True, ), PackageForTesting( "pluggy", @@ -710,7 +709,6 @@ def uninstall(self, python_cmd): "Parsed INI data: {'section': [('key', 'test1234')]}", "", test_propagation=True, - fixme_propagation_fails=True, ), PackageForTesting("psutil", "5.9.8", "cpu", "CPU Usage: replaced_usage", ""), PackageForTesting( @@ -857,10 +855,10 @@ def _assert_propagation_results(response, package): result_ok = content["result1"] == "OK" if package.fixme_propagation_fails: if result_ok: - print("FIXME: remove fixme_propagation_fails from package %s" % package.name) + pytest.fail("FIXME: remove fixme_propagation_fails from package %s" % package.name) else: - print("FIXME: propagation test (expectedly) failed for package %s" % package.name) - return + # Add pytest xfail marker to skip the test + pytest.xfail("FIXME: remove fixme_propagation_fails from package %s" % package.name) if not result_ok: print(f"Error: incorrect result from propagation endpoint for package {package.name}: {content}")