Repository: DataDog/dd-trace-py
Pull request #13155: chore(iast): add more iast tests types
Author: login: avara1986
Labels: name: changelog/no-changelog
description: A changelog entry is not required for this PR.
name: ASM
description: Application Security Monitoring
Created: 2025-04-10T12:48:42Z | Merged: 2025-04-11T14:35:58Z

## Checklist
- [x] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [x] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment
- Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)



Diff:
diff --git a/benchmarks/bm/iast_fixtures/str_methods_py3.py b/benchmarks/bm/iast_fixtures/str_methods_py3.py
index 84c14802e4d..0a6de48e114 100644
--- a/benchmarks/bm/iast_fixtures/str_methods_py3.py
+++ b/benchmarks/bm/iast_fixtures/str_methods_py3.py
@@ -1,68 +1,72 @@
 # Python 3 only functions (syntax errors on Python 2)
 import re
-from typing import TYPE_CHECKING  # noqa:F401
-
-
-if TYPE_CHECKING:  # pragma: no cover
-    from typing import Any  # noqa:F401
-    from typing import List  # noqa:F401
-    from typing import Optional  # noqa:F401
-    from typing import Tuple  # noqa:F401
+from typing import Any
+from typing import List
+from typing import Optional
 
 
 COMPILED_RE = re.compile(
-    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
+    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|"
+    r"omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|"
+    r"g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|"
+    r"l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|"  # codespell:ignore
+    r"operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|"
+    r"u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
     re.MULTILINE,
 )
 
 
-def do_zero_padding_fstring(a):  # type: (int) -> str
-    return f"{a:05d}"
+def do_fstring(a: str) -> str:
+    return f"{a}"
+
+
+def do_zero_padding_fstring(a: int, spec: str = "05d") -> str:
+    return f"{a:{spec}}"
 
 
-def do_fmt_value(a):  # type: (str) -> str
-    return f"{a:<8s}bar"
+def do_fmt_value(a: str, spec: str = "<8s") -> str:
+    return f"{a:{spec}}bar"
 
 
-def do_repr_fstring(a):  # type: (Any) -> str
+def do_repr_fstring(a: Any) -> str:
     return f"{a!r}"
 
 
-def do_repr_fstring_twice(a):  # type: (Any) -> str
+def do_repr_fstring_twice(a: Any) -> str:
     return f"{a!r} {a!r}"
 
 
-def do_repr_fstring_twice_different_objects(a, b):  # type: (Any, Any) -> str
+def do_repr_fstring_twice_different_objects(a: Any, b: Any) -> str:
     return f"{a!r} {b!r}"
 
 
-def do_repr_fstring_with_format(a):  # type: (Any) -> str
+def do_repr_fstring_with_format(a: Any) -> str:
     return f"{a!r:10}"
 
 
-def do_repr_fstring_with_format_twice(a):  # type: (Any) -> str
+def do_repr_fstring_with_format_twice(a: Any) -> str:
     return f"{a!r:10} {a!r:11}"
 
 
-def do_repr_fstring_with_expression1():  # type: (Any) -> str
+def do_repr_fstring_with_expression1() -> str:
     return f"Hello world, {False or True}!"
 
 
-def do_repr_fstring_with_expression2():  # type: (Any) -> str
+def do_repr_fstring_with_expression2() -> str:
     return f"Hello world, {'True' * 1}!"
 
 
-def do_repr_fstring_with_expression3():  # type: (Any) -> str
+def do_repr_fstring_with_expression3() -> str:
     return f"Hello world, {'true'.capitalize()}!"
 
 
-def do_repr_fstring_with_expression4():  # type: (Any) -> str
+def do_repr_fstring_with_expression4() -> str:
     import math
 
     return f"Hello world, {math.sin(5.5) <= 0}!"
 
 
-def do_repr_fstring_with_expression5():  # type: (Any) -> str
+def do_repr_fstring_with_expression5() -> str:
     return f"Hello world, {str([False, False, True, False][400 % 199]).lower().capitalize()}!"
 
 
@@ -71,7 +75,7 @@ class Resolver404(Exception):
 
 
 class ResolverMatch:
-    def __init__(self, *args, **kwargs):  # type: (List[Any], List[Any]) -> None
+    def __init__(self, *args: List[Any], **kwargs: List[Any]) -> None:
         pass
 
 
@@ -85,21 +89,21 @@ class URLPattern:
     app_name = None
     namespace = None
 
-    def __init__(self, pattern=None):  # type: (URLPattern) -> None
+    def __init__(self, pattern: Optional["URLPattern"] = None) -> None:
         self.pattern = pattern
         self.url_patterns = [self.pattern]
 
-    def _join_route(self, current_route, sub_match_route):
+    def _join_route(self, current_route: str, sub_match_route: str) -> str:
         return "".join([current_route, sub_match_route])
 
-    def match(self, path):  # type: (str) -> Optional[Tuple[str, str, str], bool]
+    def match(self, path: str):
         global COUNTER
         COUNTER = COUNTER + 1
         if COUNTER > 4:
             return False
         return path, path, path
 
-    def resolve(self, path):  # type: (str) -> Optional[ResolverMatch, None]
+    def resolve(self, path: str) -> Optional[ResolverMatch]:
         path = str(path)  # path may be a reverse_lazy object
         tried = []
         match = self.pattern.match(path) if self.pattern else False
@@ -140,7 +144,7 @@ def resolve(self, path):  # type: (str) -> Optional[ResolverMatch, None]
         raise Resolver404({"path": path})
 
 
-def do_match_group(text):
+def do_match_group(text: str) -> str:
     #
     #  TODO(avara1986): This kind of assignation doesn't work with AST patching
     #    my_re_match_function = COMPILED_RE.match
diff --git a/ddtrace/appsec/_iast/_logs.py b/ddtrace/appsec/_iast/_logs.py
index 5338e48b72c..c82617daab9 100644
--- a/ddtrace/appsec/_iast/_logs.py
+++ b/ddtrace/appsec/_iast/_logs.py
@@ -44,5 +44,5 @@ def iast_error(msg, default_prefix="iast::"):
     if _is_iast_debug_enabled():
         stack = inspect.stack()
         frame_info = "\n".join("%s %s" % (frame_info.filename, frame_info.lineno) for frame_info in stack[:7])
-        log.debug("%s. %s:\n%s", default_prefix, msg, frame_info)
+        log.debug("%s%s:\n%s", default_prefix, msg, frame_info)
     _set_iast_error_metric(f"{default_prefix}. {msg}")
diff --git a/tests/appsec/iast/aspects/test_add_aspect.py b/tests/appsec/iast/aspects/test_add_aspect.py
index fcc82ca1e28..366c12d6419 100644
--- a/tests/appsec/iast/aspects/test_add_aspect.py
+++ b/tests/appsec/iast/aspects/test_add_aspect.py
@@ -23,6 +23,11 @@
 from tests.utils import override_global_config
 
 
+@given(one_of(string_strategies))
+def test_add_aspect(text):
+    assert ddtrace_aspects.add_aspect(text, text) == text + text
+
+
 @pytest.mark.parametrize(
     "obj1, obj2",
     [
@@ -40,11 +45,6 @@ def test_add_aspect_successful(obj1, obj2):
     assert ddtrace_aspects.add_aspect(obj1, obj2) == obj1 + obj2
 
 
-@given(one_of(string_strategies))
-def test_add_aspect_text_successful(text):
-    assert ddtrace_aspects.add_aspect(text, text) == text + text
-
-
 @given(from_type(int))
 def test_add_aspect_int_successful(text):
     assert ddtrace_aspects.add_aspect(text, text) == text + text
diff --git a/tests/appsec/iast/aspects/test_add_inplace_aspect.py b/tests/appsec/iast/aspects/test_add_inplace_aspect.py
index babe9bae651..f28feef411b 100644
--- a/tests/appsec/iast/aspects/test_add_inplace_aspect.py
+++ b/tests/appsec/iast/aspects/test_add_inplace_aspect.py
@@ -1,7 +1,7 @@
-#!/usr/bin/env python3
-# -*- encoding: utf-8 -*-
 from copy import copy
 
+from hypothesis import given
+from hypothesis.strategies import one_of
 import pytest
 
 from ddtrace.appsec._iast._taint_tracking import OriginType
@@ -10,6 +10,14 @@
 from ddtrace.appsec._iast._taint_tracking._taint_objects import is_pyobject_tainted
 from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
 import ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects
+from tests.appsec.iast.iast_utils import string_strategies
+
+
+@given(one_of(string_strategies))
+def test_add_inplace_aspect(text):
+    obj3 = copy(text)
+    obj3 += text
+    assert ddtrace_aspects.add_inplace_aspect(copy(text), text) == obj3
 
 
 @pytest.mark.parametrize(
diff --git a/tests/appsec/iast/aspects/test_format_aspect_fixtures.py b/tests/appsec/iast/aspects/test_format_aspect_fixtures.py
index 5492b5f0b60..ae66f377406 100644
--- a/tests/appsec/iast/aspects/test_format_aspect_fixtures.py
+++ b/tests/appsec/iast/aspects/test_format_aspect_fixtures.py
@@ -5,6 +5,8 @@
 from typing import Any
 from typing import NamedTuple
 
+from hypothesis import given
+from hypothesis.strategies import text
 import pytest
 
 from ddtrace.appsec._iast._taint_tracking import OriginType
@@ -13,6 +15,7 @@
 from ddtrace.appsec._iast._taint_tracking._context import reset_context
 from ddtrace.appsec._iast._taint_tracking._taint_objects import get_tainted_ranges
 from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
+import ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects
 from tests.appsec.iast.aspects.aspect_utils import BaseReplacement
 from tests.appsec.iast.aspects.aspect_utils import create_taint_range_with_format
 from tests.appsec.iast.iast_utils import _iast_patched_module
@@ -24,6 +27,11 @@
 EscapeContext = NamedTuple("EscapeContext", [("id", Any), ("position", int)])
 
 
+@given(text())
+def test_format_aspect_str(text):
+    assert ddtrace_aspects.format_aspect("t-{}-t".format, 1, "t-{}-t", text) == "t-{}-t".format(text)
+
+
 class TestOperatorFormatReplacement(BaseReplacement):
     def test_format_when_template_is_none_then_raises_attribute_error(self):  # type: () -> None
         with pytest.raises(AttributeError):
diff --git a/tests/appsec/iast/aspects/test_fstrings.py b/tests/appsec/iast/aspects/test_fstrings.py
new file mode 100644
index 00000000000..f4cc5f5f8ad
--- /dev/null
+++ b/tests/appsec/iast/aspects/test_fstrings.py
@@ -0,0 +1,252 @@
+import sys
+
+from hypothesis import given
+from hypothesis.strategies import builds
+from hypothesis.strategies import integers
+from hypothesis.strategies import sampled_from
+from hypothesis.strategies import text
+import pytest
+
+from ddtrace.appsec._iast._taint_tracking import OriginType
+from ddtrace.appsec._iast._taint_tracking import as_formatted_evidence
+from ddtrace.appsec._iast._taint_tracking._taint_objects import is_pyobject_tainted
+from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
+from tests.appsec.iast.aspects.aspect_utils import BaseReplacement
+from tests.appsec.iast.aspects.aspect_utils import create_taint_range_with_format
+from tests.appsec.iast.iast_utils import CustomStr
+from tests.appsec.iast.iast_utils import _iast_patched_module
+from tests.appsec.iast.iast_utils import non_empty_text
+
+
+mod = _iast_patched_module("benchmarks.bm.iast_fixtures.str_methods")
+mod_py3 = _iast_patched_module("benchmarks.bm.iast_fixtures.str_methods_py3")
+
+
+@given(builds(CustomStr, text()))
+def test_fstring_custom_str(text):
+    result = mod_py3.do_fstring(text)
+    assert result == mod_py3.do_fstring(text)
+    assert result == f"{text}"
+
+
+@given(text())
+def test_fstring(text):
+    result = mod_py3.do_fstring(text)
+    assert result == mod_py3.do_fstring(text)
+    assert result == f"{text}"
+
+
+@pytest.mark.skipif(sys.version_info < (3, 9), reason="Python3.8 works different with fstrings")
+@given(non_empty_text)
+def test_fstring_tainted(text):
+    string_input = taint_pyobject(
+        pyobject=text, source_name="foo", source_value=text, source_origin=OriginType.PARAMETER
+    )
+    result = mod_py3.do_fstring(string_input)
+    assert result == mod_py3.do_fstring(text)
+    assert result == f"{text}"
+    assert is_pyobject_tainted(result)
+
+
+@pytest.mark.skipif(sys.version_info < (3, 9), reason="Python3.8 works different with fstrings")
+@given(non_empty_text)
+def test_fstring_fill_spaces_tainted(text):
+    string_input = taint_pyobject(
+        pyobject=text, source_name="foo", source_value=text, source_origin=OriginType.PARAMETER
+    )
+    result = mod_py3.do_fmt_value(string_input)
+    assert result == mod_py3.do_fmt_value(text)
+    assert result == f"{text:<8s}bar"
+    assert is_pyobject_tainted(result)
+
+
+@given(
+    integers(),
+    sampled_from(
+        [
+            "<8s",
+            "<1s",
+        ]
+    ),
+)
+def test_fstring_fill_spaces_integers_unkwow_format(text, spec):
+    with pytest.raises(ValueError) as excinfo:
+        f"{text:{spec}}bar"
+    assert str(excinfo.value) == "Unknown format code 's' for object of type 'int'"
+
+    with pytest.raises(ValueError) as excinfo:
+        mod_py3.do_fmt_value(text, spec)
+    assert str(excinfo.value) == "Unknown format code 's' for object of type 'int'"
+
+
+@given(
+    integers(),
+    sampled_from(
+        [
+            "!s",
+            "!s",
+        ]
+    ),
+)
+def test_fstring_fill_spaces_integers_invalid_format(text, spec):
+    with pytest.raises(ValueError) as excinfo:
+        f"{text:{spec}}bar"
+    if sys.version_info >= (3, 11):
+        assert str(excinfo.value) == "Invalid format specifier '!s' for object of type 'int'"
+    else:
+        assert str(excinfo.value) == "Invalid format specifier"
+
+    with pytest.raises(ValueError) as excinfo:
+        mod_py3.do_fmt_value(text, spec)
+
+    if sys.version_info >= (3, 11):
+        assert str(excinfo.value) == "Invalid format specifier '!s' for object of type 'int'"
+    else:
+        assert str(excinfo.value) == "Invalid format specifier"
+
+
+@pytest.mark.skipif(sys.version_info < (3, 9), reason="Python3.8 works different with fstrings")
+@given(non_empty_text)
+def test_repr_fstring_tainted(text):
+    string_input = taint_pyobject(
+        pyobject=text, source_name="foo", source_value=text, source_origin=OriginType.PARAMETER
+    )
+    result = mod_py3.do_repr_fstring(string_input)
+    assert result == mod_py3.do_repr_fstring(text)
+    assert result == f"{text!r}"
+    assert is_pyobject_tainted(result)
+
+
+@pytest.mark.skipif(sys.version_info < (3, 9), reason="Python3.8 works different with fstrings")
+@given(non_empty_text)
+def test_repr_fstring_with_format_tainted(text):
+    string_input = taint_pyobject(
+        pyobject=text, source_name="foo", source_value=text, source_origin=OriginType.PARAMETER
+    )
+    result = mod_py3.do_repr_fstring_with_format(string_input)
+    assert result == mod_py3.do_repr_fstring_with_format(text)
+    assert result == f"{text!r:10}"
+    assert is_pyobject_tainted(result)
+
+
+@given(integers())
+def test_int_fstring_zero_padding_integers(integers_to_test):
+    result = mod_py3.do_zero_padding_fstring(integers_to_test)
+    assert result == f"{integers_to_test:05d}"
+
+
+@given(
+    text(),
+    sampled_from(
+        [
+            "d",  # decimal integer
+            "f",  # float
+            "e",  # scientific notation
+            "g",  # general format
+            "b",  # binary
+            "o",  # octal
+            "x",  # hexadecimal
+            "X",  # uppercase hexadecimal
+            "n",  # number with locale
+        ]
+    ),
+)
+def test_int_fstring_zero_padding_text(text, spec):
+    print(f"{text}: {spec}")
+    with pytest.raises(ValueError) as excinfo:
+        f"{text:{spec}}"
+    assert str(excinfo.value) == f"Unknown format code '{spec}' for object of type 'str'"
+
+    with pytest.raises(ValueError) as excinfo:
+        mod_py3.do_zero_padding_fstring(text, spec)
+    assert str(excinfo.value) == f"Unknown format code '{spec}' for object of type 'str'"
+
+
+class TestOperatorsReplacement(BaseReplacement):
+    def test_string_build_string_tainted(self):
+        string_input = "foo"
+        result = mod_py3.do_fmt_value(string_input)  # pylint: disable=no-member
+        assert result == "foo     bar"
+
+        string_input = create_taint_range_with_format(":+-foo-+:")
+        result = mod_py3.do_fmt_value(string_input)  # pylint: disable=no-member
+        assert result == "foo     bar"
+        assert as_formatted_evidence(result) == ":+-foo-+:     bar"
+
+    def test_string_fstring_tainted(self):
+        string_input = "foo"
+        result = mod_py3.do_repr_fstring(string_input)
+        assert result == "'foo'"
+
+        string_input = create_taint_range_with_format(":+-foo-+:")
+
+        result = mod_py3.do_repr_fstring(string_input)  # pylint: disable=no-member
+        assert as_formatted_evidence(result) == "':+-foo-+:'"
+
+    def test_string_fstring_with_format_tainted(self):
+        string_input = "foo"
+        result = mod_py3.do_repr_fstring_with_format(string_input)
+        assert result == "'foo'     "
+
+        string_input = create_taint_range_with_format(":+-foo-+:")
+
+        result = mod_py3.do_repr_fstring_with_format(string_input)  # pylint: disable=no-member
+        assert as_formatted_evidence(result) == "':+-foo-+:'     "
+
+    def test_string_fstring_repr_str_twice_tainted(self):
+        string_input = "foo"
+
+        result = mod_py3.do_repr_fstring_twice(string_input)  # pylint: disable=no-member
+        assert result == "'foo' 'foo'"
+
+        string_input = create_taint_range_with_format(":+-foo-+:")
+
+        result = mod_py3.do_repr_fstring_twice(string_input)  # pylint: disable=no-member
+        assert result == "'foo' 'foo'"
+        assert as_formatted_evidence(result) == "':+-foo-+:' ':+-foo-+:'"
+
+    def test_string_fstring_repr_object_twice_tainted(self):
+        string_input = "foo"
+        result = mod.MyObject(string_input)
+        assert repr(result) == "foo a"
+
+        result = mod_py3.do_repr_fstring_twice(result)  # pylint: disable=no-member
+        assert result == "foo a foo a"
+
+        string_input = create_taint_range_with_format(":+-foo-+:")
+        obj = mod.MyObject(string_input)  # pylint: disable=no-member
+
+        result = mod_py3.do_repr_fstring_twice(obj)  # pylint: disable=no-member
+        assert result == "foo a foo a"
+        assert as_formatted_evidence(result) == ":+-foo-+: a :+-foo-+: a"
+
+    def test_string_fstring_twice_different_objects_tainted(self):
+        string_input = create_taint_range_with_format(":+-foo-+:")
+        obj = mod.MyObject(string_input)  # pylint: disable=no-member
+        obj2 = mod.MyObject(string_input)  # pylint: disable=no-member
+
+        result = mod_py3.do_repr_fstring_twice_different_objects(obj, obj2)  # pylint: disable=no-member
+        assert result == "foo a foo a"
+        assert as_formatted_evidence(result) == ":+-foo-+: a :+-foo-+: a"
+
+    def test_string_fstring_twice_different_objects_tainted_twice(self):
+        string_input = create_taint_range_with_format(":+-foo-+:")
+        obj = mod.MyObject(string_input)  # pylint: disable=no-member
+
+        result = mod_py3.do_repr_fstring_with_format_twice(obj)  # pylint: disable=no-member
+        assert result == "foo a      foo a      "
+        assert as_formatted_evidence(result) == ":+-foo-+: a      :+-foo-+: a      "
+
+    @pytest.mark.parametrize(
+        "function",
+        [
+            mod_py3.do_repr_fstring_with_expression1,
+            mod_py3.do_repr_fstring_with_expression2,
+            mod_py3.do_repr_fstring_with_expression3,
+            mod_py3.do_repr_fstring_with_expression4,
+            mod_py3.do_repr_fstring_with_expression5,
+        ],
+    )
+    def test_string_fstring_non_string(self, function):
+        result = function()  # pylint: disable=no-member
+        assert result == "Hello world, True!"
diff --git a/tests/appsec/iast/aspects/test_str_py3.py b/tests/appsec/iast/aspects/test_str_py3.py
deleted file mode 100644
index b4a4291ea8d..00000000000
--- a/tests/appsec/iast/aspects/test_str_py3.py
+++ /dev/null
@@ -1,130 +0,0 @@
-from hypothesis import given
-from hypothesis.strategies import text
-import pytest
-
-from ddtrace.appsec._iast._taint_tracking import as_formatted_evidence
-from tests.appsec.iast.aspects.aspect_utils import BaseReplacement
-from tests.appsec.iast.aspects.aspect_utils import create_taint_range_with_format
-from tests.appsec.iast.iast_utils import _iast_patched_module
-
-
-mod = _iast_patched_module("benchmarks.bm.iast_fixtures.str_methods")
-mod_py3 = _iast_patched_module("benchmarks.bm.iast_fixtures.str_methods_py3")
-
-
-@given(text())
-def test_int_fstring_zero_padding_text(text):
-    with pytest.raises(ValueError) as excinfo:
-        f"{text:05d}"
-    assert str(excinfo.value) == "Unknown format code 'd' for object of type 'str'"
-
-    with pytest.raises(ValueError) as excinfo:
-        mod_py3.do_zero_padding_fstring(text)
-    assert str(excinfo.value) == "Unknown format code 'd' for object of type 'str'"
-
-
-class TestOperatorsReplacement(BaseReplacement):
-    @staticmethod
-    def test_taint():  # type: () -> None
-        string_input = "foo"
-        assert as_formatted_evidence(string_input) == "foo"
-
-        string_input = create_taint_range_with_format(":+-foo-+:")
-        assert as_formatted_evidence(string_input) == ":+-foo-+:"
-
-    def test_string_build_string_tainted(self):  # type: () -> None
-        string_input = "foo"
-        result = mod_py3.do_fmt_value(string_input)  # pylint: disable=no-member
-        assert result == "foo     bar"
-
-        string_input = create_taint_range_with_format(":+-foo-+:")
-        result = mod_py3.do_fmt_value(string_input)  # pylint: disable=no-member
-        assert result == "foo     bar"
-        assert as_formatted_evidence(result) == ":+-foo-+:     bar"
-
-    def test_string_fstring_tainted(self):
-        # type: () -> None
-        string_input = "foo"
-        result = mod_py3.do_repr_fstring(string_input)
-        assert result == "'foo'"
-
-        string_input = create_taint_range_with_format(":+-foo-+:")
-
-        result = mod_py3.do_repr_fstring(string_input)  # pylint: disable=no-member
-        assert as_formatted_evidence(result) == "':+-foo-+:'"
-
-    def test_string_fstring_with_format_tainted(self):
-        # type: () -> None
-        string_input = "foo"
-        result = mod_py3.do_repr_fstring_with_format(string_input)
-        assert result == "'foo'     "
-
-        string_input = create_taint_range_with_format(":+-foo-+:")
-
-        result = mod_py3.do_repr_fstring_with_format(string_input)  # pylint: disable=no-member
-        assert as_formatted_evidence(result) == "':+-foo-+:'     "
-
-    def test_int_fstring_zero_padding_tainted(self):
-        int_input = 5
-        result = mod_py3.do_zero_padding_fstring(int_input)  # pylint: disable=no-member
-        assert result == "00005"
-
-    def test_string_fstring_repr_str_twice_tainted(self):
-        # type: () -> None
-        string_input = "foo"
-
-        result = mod_py3.do_repr_fstring_twice(string_input)  # pylint: disable=no-member
-        assert result == "'foo' 'foo'"
-
-        string_input = create_taint_range_with_format(":+-foo-+:")
-
-        result = mod_py3.do_repr_fstring_twice(string_input)  # pylint: disable=no-member
-        assert result == "'foo' 'foo'"
-        assert as_formatted_evidence(result) == "':+-foo-+:' ':+-foo-+:'"
-
-    def test_string_fstring_repr_object_twice_tainted(self):
-        # type: () -> None
-        string_input = "foo"
-        result = mod.MyObject(string_input)
-        assert repr(result) == "foo a"
-
-        result = mod_py3.do_repr_fstring_twice(result)  # pylint: disable=no-member
-        assert result == "foo a foo a"
-
-        string_input = create_taint_range_with_format(":+-foo-+:")
-        obj = mod.MyObject(string_input)  # pylint: disable=no-member
-
-        result = mod_py3.do_repr_fstring_twice(obj)  # pylint: disable=no-member
-        assert result == "foo a foo a"
-        assert as_formatted_evidence(result) == ":+-foo-+: a :+-foo-+: a"
-
-    def test_string_fstring_twice_different_objects_tainted(self):  # type: () -> None
-        string_input = create_taint_range_with_format(":+-foo-+:")
-        obj = mod.MyObject(string_input)  # pylint: disable=no-member
-        obj2 = mod.MyObject(string_input)  # pylint: disable=no-member
-
-        result = mod_py3.do_repr_fstring_twice_different_objects(obj, obj2)  # pylint: disable=no-member
-        assert result == "foo a foo a"
-        assert as_formatted_evidence(result) == ":+-foo-+: a :+-foo-+: a"
-
-    def test_string_fstring_twice_different_objects_tainted_twice(self):  # type: () -> None
-        string_input = create_taint_range_with_format(":+-foo-+:")
-        obj = mod.MyObject(string_input)  # pylint: disable=no-member
-
-        result = mod_py3.do_repr_fstring_with_format_twice(obj)  # pylint: disable=no-member
-        assert result == "foo a      foo a      "
-        assert as_formatted_evidence(result) == ":+-foo-+: a      :+-foo-+: a      "
-
-    @pytest.mark.parametrize(
-        "function",
-        [
-            mod_py3.do_repr_fstring_with_expression1,
-            mod_py3.do_repr_fstring_with_expression2,
-            mod_py3.do_repr_fstring_with_expression3,
-            mod_py3.do_repr_fstring_with_expression4,
-            mod_py3.do_repr_fstring_with_expression5,
-        ],
-    )
-    def test_string_fstring_non_string(self, function):  # type: () -> None
-        result = function()  # pylint: disable=no-member
-        assert result == "Hello world, True!"
diff --git a/tests/appsec/iast/iast_utils.py b/tests/appsec/iast/iast_utils.py
index 72df12a74fd..8c9e526f8b3 100644
--- a/tests/appsec/iast/iast_utils.py
+++ b/tests/appsec/iast/iast_utils.py
@@ -73,7 +73,7 @@ def _iast_patched_module(module_name, new_module_object=False):
 
 
 class CustomStr(str):
-    pass
+    __slots__ = ()
 
 
 class CustomBytes(bytes):
@@ -84,7 +84,8 @@ class CustomBytearray(bytearray):
     pass
 
 
-non_empty_text = text().filter(lambda x: x not in ("", "0", "1", "2"))
+non_empty_text = text().filter(lambda x: x not in ("",))
+non_empty_binary = binary().filter(lambda x: x not in (b"",))
 
 string_strategies: List[Any] = [
     text(),  # regular str
@@ -94,3 +95,9 @@ class CustomBytearray(bytearray):
     builds(CustomBytes, binary()),  # custom bytes subclass
     builds(CustomBytearray, binary()),  # custom bytearray subclass
 ]
+
+string_valid_to_taint_strategies: List[Any] = [
+    non_empty_text,  # regular str
+    non_empty_binary,  # regular bytes
+    builds(bytearray, non_empty_binary),  # regular bytearray
+]
diff --git a/tests/appsec/iast/taint_tracking/test_taint_tracking.py b/tests/appsec/iast/taint_tracking/test_taint_tracking.py
index f73a7e6604e..b5741a421ec 100644
--- a/tests/appsec/iast/taint_tracking/test_taint_tracking.py
+++ b/tests/appsec/iast/taint_tracking/test_taint_tracking.py
@@ -1,22 +1,42 @@
-#!/usr/bin/env python3
 import logging
+import sys
 
+from hypothesis import given
+from hypothesis.strategies import one_of
 import pytest
 
+from ddtrace.appsec._iast._taint_tracking import OriginType
+from ddtrace.appsec._iast._taint_tracking import TaintRange
+from ddtrace.appsec._iast._taint_tracking import num_objects_tainted
+from ddtrace.appsec._iast._taint_tracking import set_ranges
+from ddtrace.appsec._iast._taint_tracking._context import reset_context
+from ddtrace.appsec._iast._taint_tracking._taint_objects import is_pyobject_tainted
+from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
+from ddtrace.appsec._iast._taint_tracking.aspects import add_aspect
 from ddtrace.appsec._iast.reporter import IastSpanReporter
 from ddtrace.appsec._iast.reporter import Source
+from tests.appsec.iast.iast_utils import non_empty_text
+from tests.appsec.iast.iast_utils import string_strategies
 from tests.utils import override_env
 from tests.utils import override_global_config
 
 
-with override_env({"DD_IAST_ENABLED": "True"}):
-    from ddtrace.appsec._iast._taint_tracking import OriginType
-    from ddtrace.appsec._iast._taint_tracking import TaintRange
-    from ddtrace.appsec._iast._taint_tracking import num_objects_tainted
-    from ddtrace.appsec._iast._taint_tracking import set_ranges
-    from ddtrace.appsec._iast._taint_tracking._context import reset_context
-    from ddtrace.appsec._iast._taint_tracking._taint_objects import taint_pyobject
-    from ddtrace.appsec._iast._taint_tracking.aspects import add_aspect
+@pytest.mark.skipif(sys.version_info < (3, 9), reason="Python3.8 works different with fstrings")
+@given(non_empty_text)
+def test_taint_pyobject(text_to_taint):
+    tainted_text = taint_pyobject(
+        text_to_taint, source_name="request_body", source_value=text_to_taint, source_origin=OriginType.PARAMETER
+    )
+    assert is_pyobject_tainted(tainted_text)
+
+
+@pytest.mark.skip_iast_check_logs
+@given(one_of(string_strategies))
+def test_taint_pyobject_all(text_to_taint):
+    tainted_text = taint_pyobject(
+        text_to_taint, source_name="request_body", source_value=text_to_taint, source_origin=OriginType.PARAMETER
+    )
+    assert tainted_text == text_to_taint
 
 
 def test_taint_ranges_as_evidence_info_nothing_tainted():


Reviews:
user: login: datadog-datadog-prod-us1[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T14:25:33Z
user: login: christophe-papazian
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T13:50:37Z

Review comments:
diff_hunk: @@ -1,68 +1,73 @@
 # Python 3 only functions (syntax errors on Python 2)
 import re
-from typing import TYPE_CHECKING  # noqa:F401
-
-
-if TYPE_CHECKING:  # pragma: no cover
-    from typing import Any  # noqa:F401
-    from typing import List  # noqa:F401
-    from typing import Optional  # noqa:F401
-    from typing import Tuple  # noqa:F401
+from typing import Any
+from typing import List
+from typing import Optional
+from typing import Tuple
 
 
 COMPILED_RE = re.compile(
-    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
+    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|"
+    r"omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|"
+    r"g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|"
+    r"l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|"  # codespell:ignore
+    r"operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|"
+    r"u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
     re.MULTILINE,
 )
 
 
-def do_zero_padding_fstring(a):  # type: (int) -> str
-    return f"{a:05d}"
+def do_fstring(a: str) -> str:
+    return f"{a}"
+
+
+def do_zero_padding_fstring(a: int, spec: str = "05d") -> str:
+    return f"{a:{spec}}"
 
 
-def do_fmt_value(a):  # type: (str) -> str
-    return f"{a:<8s}bar"
+def do_fmt_value(a: str, spec: str = "<8s") -> str:
+    return f"{a:{spec}}bar"
 
 
-def do_repr_fstring(a):  # type: (Any) -> str
+def do_repr_fstring(a: Any) -> str:
     return f"{a!r}"
 
 
-def do_repr_fstring_twice(a):  # type: (Any) -> str
+def do_repr_fstring_twice(a: Any) -> str:
     return f"{a!r} {a!r}"
 
 
-def do_repr_fstring_twice_different_objects(a, b):  # type: (Any, Any) -> str
+def do_repr_fstring_twice_different_objects(a: Any, b: Any) -> str:
     return f"{a!r} {b!r}"
 
 
-def do_repr_fstring_with_format(a):  # type: (Any) -> str
+def do_repr_fstring_with_format(a: Any) -> str:
path: benchmarks/bm/iast_fixtures/str_methods_py3.py
user: login: datadog-datadog-prod-us1[bot]
body: <!-- python-best-practices/any-type-disallow -->

<h3><span title="Warning"> :orange_circle: </span>Code Quality Violation</h3>



<details>
  <summary>
    do not use Any, use a concrete type <i>(...read more)</i>
  </summary>

<br>

Use the `Any` type very carefully. Most of the time, the `Any` type is used because we do not know exactly what type is being used. If you want to specify that a value can be of any type, use `object` instead of `Any`.


#### Learn More

 - [Python documentation: the `Any` type](https://docs.python.org/3/library/typing.html#the-any-type)
</details>

<p align="right"><a href="https://app.datadoghq.com/ci/code-analysis/github.com%2Fdatadog%2Fdd-trace-py/avara1986%2Favara1986%2Fimprove_iast_tests_2/d09f922f23c08347629f53e159c94d099d42d15b/code-quality?index=cicodescan&query=%40git.repository.id%3A%22github.com%2Fdatadog%2Fdd-trace-py%22+%40git.branch%3A%22avara1986%2Favara1986%2Fimprove_iast_tests_2%22+%40git.commit.sha%3A%22d09f922f23c08347629f53e159c94d099d42d15b%22+-%40static_analysis.result.category%3Asecurity+-%40static_analysis.result.item_type%3Asecret&staticAnalysisId=AgAAAZYgFwPQxstlegAAAAAAAAAYAAAAAEFaWWdGdmxnQUFCWW1OdThxcUtVQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2&trace=AgAAAZYgFwPQxstlegAAAAAAAAAYAAAAAEFaWWdGdmxnQUFCWW1OdThxcUtVQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2" title="View in Datadog"><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/datadog_icon.svg" alt="View in Datadog" align="center" /></a>&nbsp;&nbsp;<a href="https://dd.enterprise.slack.com/archives/C03NGS0N3GA" title="Leave us feedback"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_light.svg" alt="Leave us feedback" align="center" /></picture></a>&nbsp;&nbsp;<a href="https://docs.datadoghq.com/static_analysis/rules/python-best-practices/any-type-disallow" title="Documentation"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_light.svg" alt="Documentation" align="center" /></picture></a></p>

created_at: 2025-04-10T14:25:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,68 +1,73 @@
 # Python 3 only functions (syntax errors on Python 2)
 import re
-from typing import TYPE_CHECKING  # noqa:F401
-
-
-if TYPE_CHECKING:  # pragma: no cover
-    from typing import Any  # noqa:F401
-    from typing import List  # noqa:F401
-    from typing import Optional  # noqa:F401
-    from typing import Tuple  # noqa:F401
+from typing import Any
+from typing import List
+from typing import Optional
+from typing import Tuple
 
 
 COMPILED_RE = re.compile(
-    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
+    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|"
+    r"omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|"
+    r"g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|"
+    r"l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|"  # codespell:ignore
+    r"operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|"
+    r"u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
     re.MULTILINE,
 )
 
 
-def do_zero_padding_fstring(a):  # type: (int) -> str
-    return f"{a:05d}"
+def do_fstring(a: str) -> str:
+    return f"{a}"
+
+
+def do_zero_padding_fstring(a: int, spec: str = "05d") -> str:
+    return f"{a:{spec}}"
 
 
-def do_fmt_value(a):  # type: (str) -> str
-    return f"{a:<8s}bar"
+def do_fmt_value(a: str, spec: str = "<8s") -> str:
+    return f"{a:{spec}}bar"
 
 
-def do_repr_fstring(a):  # type: (Any) -> str
+def do_repr_fstring(a: Any) -> str:
     return f"{a!r}"
 
 
-def do_repr_fstring_twice(a):  # type: (Any) -> str
+def do_repr_fstring_twice(a: Any) -> str:
     return f"{a!r} {a!r}"
 
 
-def do_repr_fstring_twice_different_objects(a, b):  # type: (Any, Any) -> str
+def do_repr_fstring_twice_different_objects(a: Any, b: Any) -> str:
     return f"{a!r} {b!r}"
 
 
-def do_repr_fstring_with_format(a):  # type: (Any) -> str
+def do_repr_fstring_with_format(a: Any) -> str:
     return f"{a!r:10}"
 
 
-def do_repr_fstring_with_format_twice(a):  # type: (Any) -> str
+def do_repr_fstring_with_format_twice(a: Any) -> str:
path: benchmarks/bm/iast_fixtures/str_methods_py3.py
user: login: datadog-datadog-prod-us1[bot]
body: <!-- python-best-practices/any-type-disallow -->

<h3><span title="Warning"> :orange_circle: </span>Code Quality Violation</h3>



<details>
  <summary>
    do not use Any, use a concrete type <i>(...read more)</i>
  </summary>

<br>

Use the `Any` type very carefully. Most of the time, the `Any` type is used because we do not know exactly what type is being used. If you want to specify that a value can be of any type, use `object` instead of `Any`.


#### Learn More

 - [Python documentation: the `Any` type](https://docs.python.org/3/library/typing.html#the-any-type)
</details>

<p align="right"><a href="https://app.datadoghq.com/ci/code-analysis/github.com%2Fdatadog%2Fdd-trace-py/avara1986%2Favara1986%2Fimprove_iast_tests_2/d09f922f23c08347629f53e159c94d099d42d15b/code-quality?index=cicodescan&query=%40git.repository.id%3A%22github.com%2Fdatadog%2Fdd-trace-py%22+%40git.branch%3A%22avara1986%2Favara1986%2Fimprove_iast_tests_2%22+%40git.commit.sha%3A%22d09f922f23c08347629f53e159c94d099d42d15b%22+-%40static_analysis.result.category%3Asecurity+-%40static_analysis.result.item_type%3Asecret&staticAnalysisId=AgAAAZYgFwPQxstlwgAAAAAAAAAYAAAAAEFaWWdGdmxnQUFBcjJaYWl3TUpGQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2&trace=AgAAAZYgFwPQxstlwgAAAAAAAAAYAAAAAEFaWWdGdmxnQUFBcjJaYWl3TUpGQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2" title="View in Datadog"><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/datadog_icon.svg" alt="View in Datadog" align="center" /></a>&nbsp;&nbsp;<a href="https://dd.enterprise.slack.com/archives/C03NGS0N3GA" title="Leave us feedback"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_light.svg" alt="Leave us feedback" align="center" /></picture></a>&nbsp;&nbsp;<a href="https://docs.datadoghq.com/static_analysis/rules/python-best-practices/any-type-disallow" title="Documentation"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_light.svg" alt="Documentation" align="center" /></picture></a></p>

created_at: 2025-04-10T14:25:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,68 +1,73 @@
 # Python 3 only functions (syntax errors on Python 2)
 import re
-from typing import TYPE_CHECKING  # noqa:F401
-
-
-if TYPE_CHECKING:  # pragma: no cover
-    from typing import Any  # noqa:F401
-    from typing import List  # noqa:F401
-    from typing import Optional  # noqa:F401
-    from typing import Tuple  # noqa:F401
+from typing import Any
+from typing import List
+from typing import Optional
+from typing import Tuple
 
 
 COMPILED_RE = re.compile(
-    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
+    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|"
+    r"omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|"
+    r"g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|"
+    r"l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|"  # codespell:ignore
+    r"operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|"
+    r"u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
     re.MULTILINE,
 )
 
 
-def do_zero_padding_fstring(a):  # type: (int) -> str
-    return f"{a:05d}"
+def do_fstring(a: str) -> str:
+    return f"{a}"
+
+
+def do_zero_padding_fstring(a: int, spec: str = "05d") -> str:
+    return f"{a:{spec}}"
 
 
-def do_fmt_value(a):  # type: (str) -> str
-    return f"{a:<8s}bar"
+def do_fmt_value(a: str, spec: str = "<8s") -> str:
+    return f"{a:{spec}}bar"
 
 
-def do_repr_fstring(a):  # type: (Any) -> str
+def do_repr_fstring(a: Any) -> str:
     return f"{a!r}"
 
 
-def do_repr_fstring_twice(a):  # type: (Any) -> str
+def do_repr_fstring_twice(a: Any) -> str:
path: benchmarks/bm/iast_fixtures/str_methods_py3.py
user: login: datadog-datadog-prod-us1[bot]
body: <!-- python-best-practices/any-type-disallow -->

<h3><span title="Warning"> :orange_circle: </span>Code Quality Violation</h3>



<details>
  <summary>
    do not use Any, use a concrete type <i>(...read more)</i>
  </summary>

<br>

Use the `Any` type very carefully. Most of the time, the `Any` type is used because we do not know exactly what type is being used. If you want to specify that a value can be of any type, use `object` instead of `Any`.


#### Learn More

 - [Python documentation: the `Any` type](https://docs.python.org/3/library/typing.html#the-any-type)
</details>

<p align="right"><a href="https://app.datadoghq.com/ci/code-analysis/github.com%2Fdatadog%2Fdd-trace-py/avara1986%2Favara1986%2Fimprove_iast_tests_2/d09f922f23c08347629f53e159c94d099d42d15b/code-quality?index=cicodescan&query=%40git.repository.id%3A%22github.com%2Fdatadog%2Fdd-trace-py%22+%40git.branch%3A%22avara1986%2Favara1986%2Fimprove_iast_tests_2%22+%40git.commit.sha%3A%22d09f922f23c08347629f53e159c94d099d42d15b%22+-%40static_analysis.result.category%3Asecurity+-%40static_analysis.result.item_type%3Asecret&staticAnalysisId=AgAAAZYgFwPQxstmAgAAAAAAAAAYAAAAAEFaWWdGdmxnQUFCV2d2WkMwcU5iQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2&trace=AgAAAZYgFwPQxstmAgAAAAAAAAAYAAAAAEFaWWdGdmxnQUFCV2d2WkMwcU5iQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2" title="View in Datadog"><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/datadog_icon.svg" alt="View in Datadog" align="center" /></a>&nbsp;&nbsp;<a href="https://dd.enterprise.slack.com/archives/C03NGS0N3GA" title="Leave us feedback"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_light.svg" alt="Leave us feedback" align="center" /></picture></a>&nbsp;&nbsp;<a href="https://docs.datadoghq.com/static_analysis/rules/python-best-practices/any-type-disallow" title="Documentation"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_light.svg" alt="Documentation" align="center" /></picture></a></p>

created_at: 2025-04-10T14:25:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,68 +1,73 @@
 # Python 3 only functions (syntax errors on Python 2)
 import re
-from typing import TYPE_CHECKING  # noqa:F401
-
-
-if TYPE_CHECKING:  # pragma: no cover
-    from typing import Any  # noqa:F401
-    from typing import List  # noqa:F401
-    from typing import Optional  # noqa:F401
-    from typing import Tuple  # noqa:F401
+from typing import Any
+from typing import List
+from typing import Optional
+from typing import Tuple
 
 
 COMPILED_RE = re.compile(
-    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
+    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|"
+    r"omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|"
+    r"g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|"
+    r"l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|"  # codespell:ignore
+    r"operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|"
+    r"u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
     re.MULTILINE,
 )
 
 
-def do_zero_padding_fstring(a):  # type: (int) -> str
-    return f"{a:05d}"
+def do_fstring(a: str) -> str:
+    return f"{a}"
+
+
+def do_zero_padding_fstring(a: int, spec: str = "05d") -> str:
+    return f"{a:{spec}}"
 
 
-def do_fmt_value(a):  # type: (str) -> str
-    return f"{a:<8s}bar"
+def do_fmt_value(a: str, spec: str = "<8s") -> str:
+    return f"{a:{spec}}bar"
 
 
-def do_repr_fstring(a):  # type: (Any) -> str
+def do_repr_fstring(a: Any) -> str:
path: benchmarks/bm/iast_fixtures/str_methods_py3.py
user: login: datadog-datadog-prod-us1[bot]
body: <!-- python-best-practices/any-type-disallow -->

<h3><span title="Warning"> :orange_circle: </span>Code Quality Violation</h3>



<details>
  <summary>
    do not use Any, use a concrete type <i>(...read more)</i>
  </summary>

<br>

Use the `Any` type very carefully. Most of the time, the `Any` type is used because we do not know exactly what type is being used. If you want to specify that a value can be of any type, use `object` instead of `Any`.


#### Learn More

 - [Python documentation: the `Any` type](https://docs.python.org/3/library/typing.html#the-any-type)
</details>

<p align="right"><a href="https://app.datadoghq.com/ci/code-analysis/github.com%2Fdatadog%2Fdd-trace-py/avara1986%2Favara1986%2Fimprove_iast_tests_2/d09f922f23c08347629f53e159c94d099d42d15b/code-quality?index=cicodescan&query=%40git.repository.id%3A%22github.com%2Fdatadog%2Fdd-trace-py%22+%40git.branch%3A%22avara1986%2Favara1986%2Fimprove_iast_tests_2%22+%40git.commit.sha%3A%22d09f922f23c08347629f53e159c94d099d42d15b%22+-%40static_analysis.result.category%3Asecurity+-%40static_analysis.result.item_type%3Asecret&staticAnalysisId=AgAAAZYgFwPQxstkswAAAAAAAAAYAAAAAEFaWWdGdmxnQUFDbHdOWEdXYS1kQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2&trace=AgAAAZYgFwPQxstkswAAAAAAAAAYAAAAAEFaWWdGdmxnQUFDbHdOWEdXYS1kQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2" title="View in Datadog"><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/datadog_icon.svg" alt="View in Datadog" align="center" /></a>&nbsp;&nbsp;<a href="https://dd.enterprise.slack.com/archives/C03NGS0N3GA" title="Leave us feedback"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_light.svg" alt="Leave us feedback" align="center" /></picture></a>&nbsp;&nbsp;<a href="https://docs.datadoghq.com/static_analysis/rules/python-best-practices/any-type-disallow" title="Documentation"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_light.svg" alt="Documentation" align="center" /></picture></a></p>

created_at: 2025-04-10T14:25:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,68 +1,73 @@
 # Python 3 only functions (syntax errors on Python 2)
 import re
-from typing import TYPE_CHECKING  # noqa:F401
-
-
-if TYPE_CHECKING:  # pragma: no cover
-    from typing import Any  # noqa:F401
-    from typing import List  # noqa:F401
-    from typing import Optional  # noqa:F401
-    from typing import Tuple  # noqa:F401
+from typing import Any
+from typing import List
+from typing import Optional
+from typing import Tuple
 
 
 COMPILED_RE = re.compile(
-    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
+    r"(?<!\.)(__import__|a(?:bs|iter|ll|ny)|b(?:in|ool|reakpoint|yte(?:array|s))|c(?:allable|hr|lassmethod|"
+    r"omp(?:ile|lex))|d(?:elattr|i(?:ct|r|vmod))|e(?:numerate|val)|f(?:ilter|(?:loa|orma|rozense)t)|"
+    r"g(?:etattr|lobals)|h(?:as(?:attr|h)|ex)|i(?:d|n(?:(?:(?:pu)?)t)|s(?:instance|subclass)|ter)|"
+    r"l(?:en|ist|ocals)|m(?:a(?:[px])|emoryview|in)|next|o(?:bject|ct|pen|rd)|p(?:ow|r(?:int|"  # codespell:ignore
+    r"operty))|r(?:ange|e(?:pr|versed)|ound)|s(?:et(?:(?:attr)?)|lice|orted|t(?:aticmethod|r)|"
+    r"u(?:m|per))|t(?:(?:upl|yp)e)|vars|zip)\b",
     re.MULTILINE,
 )
 
 
-def do_zero_padding_fstring(a):  # type: (int) -> str
-    return f"{a:05d}"
+def do_fstring(a: str) -> str:
+    return f"{a}"
+
+
+def do_zero_padding_fstring(a: int, spec: str = "05d") -> str:
+    return f"{a:{spec}}"
 
 
-def do_fmt_value(a):  # type: (str) -> str
-    return f"{a:<8s}bar"
+def do_fmt_value(a: str, spec: str = "<8s") -> str:
+    return f"{a:{spec}}bar"
 
 
-def do_repr_fstring(a):  # type: (Any) -> str
+def do_repr_fstring(a: Any) -> str:
     return f"{a!r}"
 
 
-def do_repr_fstring_twice(a):  # type: (Any) -> str
+def do_repr_fstring_twice(a: Any) -> str:
     return f"{a!r} {a!r}"
 
 
-def do_repr_fstring_twice_different_objects(a, b):  # type: (Any, Any) -> str
+def do_repr_fstring_twice_different_objects(a: Any, b: Any) -> str:
path: benchmarks/bm/iast_fixtures/str_methods_py3.py
user: login: datadog-datadog-prod-us1[bot]
body: <!-- python-best-practices/any-type-disallow -->

<h3><span title="Warning"> :orange_circle: </span>Code Quality Violation</h3>



<details>
  <summary>
    do not use Any, use a concrete type <i>(...read more)</i>
  </summary>

<br>

Use the `Any` type very carefully. Most of the time, the `Any` type is used because we do not know exactly what type is being used. If you want to specify that a value can be of any type, use `object` instead of `Any`.


#### Learn More

 - [Python documentation: the `Any` type](https://docs.python.org/3/library/typing.html#the-any-type)
</details>

<p align="right"><a href="https://app.datadoghq.com/ci/code-analysis/github.com%2Fdatadog%2Fdd-trace-py/avara1986%2Favara1986%2Fimprove_iast_tests_2/d09f922f23c08347629f53e159c94d099d42d15b/code-quality?index=cicodescan&query=%40git.repository.id%3A%22github.com%2Fdatadog%2Fdd-trace-py%22+%40git.branch%3A%22avara1986%2Favara1986%2Fimprove_iast_tests_2%22+%40git.commit.sha%3A%22d09f922f23c08347629f53e159c94d099d42d15b%22+-%40static_analysis.result.category%3Asecurity+-%40static_analysis.result.item_type%3Asecret&staticAnalysisId=AgAAAZYgFwPQxstlGgAAAAAAAAAYAAAAAEFaWWdGdmxnQUFCUUFuNlR3Vm9iQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2&trace=AgAAAZYgFwPQxstlGgAAAAAAAAAYAAAAAEFaWWdGdmxnQUFCUUFuNlR3Vm9iQUFBQQAAACQAAAAAMDE5NjIwMTctMTMxNi00YjA3LWJjMjEtYTUyOGYyY2ViMGI2" title="View in Datadog"><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/datadog_icon.svg" alt="View in Datadog" align="center" /></a>&nbsp;&nbsp;<a href="https://dd.enterprise.slack.com/archives/C03NGS0N3GA" title="Leave us feedback"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/comment_light.svg" alt="Leave us feedback" align="center" /></picture></a>&nbsp;&nbsp;<a href="https://docs.datadoghq.com/static_analysis/rules/python-best-practices/any-type-disallow" title="Documentation"><picture><source media="(prefers-color-scheme: dark)" srcset="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_dark.svg"/><img src="https://app.datadoghq.com/api/ui/asset/images/pr_comments/software_delivery/books_light.svg" alt="Documentation" align="center" /></picture></a></p>

created_at: 2025-04-10T14:25:33Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-10T12:49:17Z
body: `CODEOWNERS` have been resolved as:

```
tests/appsec/iast/aspects/test_fstrings.py                              @DataDog/asm-python
benchmarks/bm/iast_fixtures/str_methods_py3.py                          @DataDog/asm-python
ddtrace/appsec/_iast/_logs.py                                           @DataDog/asm-python
tests/appsec/iast/aspects/test_add_aspect.py                            @DataDog/asm-python
tests/appsec/iast/aspects/test_add_inplace_aspect.py                    @DataDog/asm-python
tests/appsec/iast/aspects/test_format_aspect_fixtures.py                @DataDog/asm-python
tests/appsec/iast/iast_utils.py                                         @DataDog/asm-python
tests/appsec/iast/taint_tracking/test_taint_tracking.py                 @DataDog/asm-python
```
<!-- thollander/actions-comment-pull-request "codeowners_resolved" -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-10T13:11:03Z
body: ## Bootstrap import analysis

Comparison of import times between this PR and base.

### Summary

The average import time from this PR is: 233 ± 4 ms.

The average import time from base is: 234 ± 3 ms.

The import time difference between this PR and base is: -1.2 ± 0.2 ms.


### Import time breakdown

The following import paths have **shrunk**:
<details style="padding-left: 1em"><summary><progress value="1889.6313686313695" max="232942.1118881119" style="width: 2em;"></progress>
            <code>ddtrace.auto</code>
            <span style="color: #888; font-size:small; font-family: arial;">1.890 ms</span>
            <span style="color: #888; font-size:small; font-family: arial;">(0.81%)</span>
        </summary><details style="padding-left: 1em"><summary><progress value="1226.4645354645363" max="232942.1118881119" style="width: 2em;"></progress>
            <code>ddtrace.bootstrap.sitecustomize</code>
            <span style="color: #888; font-size:small; font-family: arial;">1.226 ms</span>
            <span style="color: #888; font-size:small; font-family: arial;">(0.53%)</span>
        </summary><details style="padding-left: 1em"><summary><progress value="1226.4645354645363" max="232942.1118881119" style="width: 2em;"></progress>
            <code>ddtrace.bootstrap.preload</code>
            <span style="color: #888; font-size:small; font-family: arial;">1.226 ms</span>
            <span style="color: #888; font-size:small; font-family: arial;">(0.53%)</span>
        </summary><details style="padding-left: 1em"><summary><progress value="1226.4645354645363" max="232942.1118881119" style="width: 2em;"></progress>
            <code>ddtrace.internal.products</code>
            <span style="color: #888; font-size:small; font-family: arial;">1.226 ms</span>
            <span style="color: #888; font-size:small; font-family: arial;">(0.53%)</span>
        </summary><div style="padding-left: 2em"><progress value="600.6373626373625" max="232942.1118881119" style="width: 2em;"></progress>
            <code>ddtrace.internal.remoteconfig.client</code>
            <span style="color: #888; font-size:small; font-family: arial;">0.601 ms</span>
            <span style="color: #888; font-size:small; font-family: arial;">(0.26%)</span>
        </div></details>
</details>
</details>
<div style="padding-left: 2em"><progress value="663.1668331668332" max="232942.1118881119" style="width: 2em;"></progress>
            <code>ddtrace</code>
            <span style="color: #888; font-size:small; font-family: arial;">0.663 ms</span>
            <span style="color: #888; font-size:small; font-family: arial;">(0.28%)</span>
        </div></details>



<!-- thollander/actions-comment-pull-request "import_analysis" -->
author_association: CONTRIBUTOR
user: login: pr-commenter[bot]
created_at: 2025-04-10T13:33:00Z
body: ## Benchmarks

Benchmark execution time: 2025-04-11 14:06:22

Comparing candidate commit ce25aa69475c5da5737f97b037a7c9f7154d10a9 in PR branch `avara1986/avara1986/improve_iast_tests_2` with baseline commit b1950af570271cc1a17e3b3c484aa9613df72002 in branch `main`.

Found 0 performance improvements and 1 performance regressions! Performance is the same for 501 metrics, 2 unstable metrics.

#### scenario:iast_aspects-format_map_aspect

- 🟥 `execution_time` [+425.058ns; +494.241ns] or [+12.912%; +15.013%]




author_association: NONE