From fc4e7025cd52f01a1b6f95c1071259b4eb52a406 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Tue, 17 Sep 2024 17:20:37 +0200 Subject: [PATCH 01/14] chore(iast): BytesIO and StringIO aspects --- ddtrace/appsec/_constants.py | 4 +- ddtrace/appsec/_iast/_ast/ast_patching.py | 1 - ddtrace/appsec/_iast/_ast/visitor.py | 6 ++ .../_iast/_taint_tracking/Utils/StringUtils.h | 8 ++- .../appsec/_iast/_taint_tracking/__init__.py | 7 ++- .../appsec/_iast/_taint_tracking/aspects.py | 56 +++++++++++++++++ tests/appsec/app.py | 63 +++++++++++++++++++ tests/appsec/iast_packages/test_packages.py | 1 - .../integrations/test_flask_iast_patching.py | 24 +++++++ 9 files changed, 164 insertions(+), 6 deletions(-) diff --git a/ddtrace/appsec/_constants.py b/ddtrace/appsec/_constants.py index 7bf3a96bda9..568b98d9891 100644 --- a/ddtrace/appsec/_constants.py +++ b/ddtrace/appsec/_constants.py @@ -1,4 +1,6 @@ import os +from _io import BytesIO +from _io import StringIO from re import Match import sys @@ -122,7 +124,7 @@ class IAST(metaclass=Constant_Class): SEP_MODULES: Literal[","] = "," REQUEST_IAST_ENABLED: Literal["_dd.iast.request_enabled"] = "_dd.iast.request_enabled" TEXT_TYPES = (str, bytes, bytearray) - TAINTEABLE_TYPES = (str, bytes, bytearray, Match) + TAINTEABLE_TYPES = (str, bytes, bytearray, Match, BytesIO, StringIO) class IAST_SPAN_TAGS(metaclass=Constant_Class): diff --git a/ddtrace/appsec/_iast/_ast/ast_patching.py b/ddtrace/appsec/_iast/_ast/ast_patching.py index 0c409ee6837..572fca02ce6 100644 --- a/ddtrace/appsec/_iast/_ast/ast_patching.py +++ b/ddtrace/appsec/_iast/_ast/ast_patching.py @@ -262,7 +262,6 @@ "cattrs.", "ddsketch.", "ddtrace.", - "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 diff --git a/ddtrace/appsec/_iast/_ast/visitor.py b/ddtrace/appsec/_iast/_ast/visitor.py index 6d07aefde8b..410111edf7b 100644 --- a/ddtrace/appsec/_iast/_ast/visitor.py +++ b/ddtrace/appsec/_iast/_ast/visitor.py @@ -40,12 +40,18 @@ def _mark_avoid_convert_recursively(node): "definitions_module": "ddtrace.appsec._iast._taint_tracking.aspects", "alias_module": "ddtrace_aspects", "functions": { + "StringIO": "ddtrace_aspects.stringio_aspect", + "BytesIO": "ddtrace_aspects.bytesio_aspect", + "read": "ddtrace_aspects.read_aspect", "str": "ddtrace_aspects.str_aspect", "bytes": "ddtrace_aspects.bytes_aspect", "bytearray": "ddtrace_aspects.bytearray_aspect", "ddtrace_iast_flask_patch": "ddtrace_aspects.empty_func", # To avoid recursion }, "stringalike_methods": { + "StringIO": "ddtrace_aspects.stringio_aspect", + "BytesIO": "ddtrace_aspects.bytesio_aspect", + "read": "ddtrace_aspects.read_aspect", "decode": "ddtrace_aspects.decode_aspect", "join": "ddtrace_aspects.join_aspect", "encode": "ddtrace_aspects.encode_aspect", diff --git a/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h b/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h index 6ff008337c4..c746a5ad0bc 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h +++ b/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h @@ -32,6 +32,12 @@ get_unique_id(const PyObject* str) return reinterpret_cast(str); } +static bool +PyIOBase_Check(const PyObject* obj) +{ + return py::isinstance((PyObject*)obj, py::module_::import("_io").attr("_IOBase")); +} + static bool PyReMatch_Check(const PyObject* obj) { @@ -53,7 +59,7 @@ is_text(const PyObject* pyptr) inline bool is_tainteable(const PyObject* pyptr) { - return pyptr != nullptr and (is_text(pyptr) or PyReMatch_Check(pyptr)); + return pyptr != nullptr and (is_text(pyptr) or PyReMatch_Check(pyptr) or PyIOBase_Check(pyptr)); } // Base function for the variadic template diff --git a/ddtrace/appsec/_iast/_taint_tracking/__init__.py b/ddtrace/appsec/_iast/_taint_tracking/__init__.py index 059c37b452d..4ce158cfeaf 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/__init__.py +++ b/ddtrace/appsec/_iast/_taint_tracking/__init__.py @@ -1,3 +1,6 @@ +from io import BytesIO +from io import StringIO + import os from typing import Any from typing import Tuple @@ -220,9 +223,9 @@ def trace_calls_and_returns(frame, event, arg): if frame in TAINTED_FRAMES: TAINTED_FRAMES.remove(frame) log.debug("Return from %s on line %d of %s, return value: %s", func_name, line_no, filename, arg) - if isinstance(arg, (str, bytes, bytearray, list, tuple, dict)): + if isinstance(arg, (str, bytes, bytearray, BytesIO, StringIO, list, tuple, dict)): if ( - (isinstance(arg, (str, bytes, bytearray)) and is_pyobject_tainted(arg)) + (isinstance(arg, (str, bytes, bytearray, BytesIO, StringIO)) and is_pyobject_tainted(arg)) or (isinstance(arg, (list, tuple)) and any([is_pyobject_tainted(x) for x in arg])) or (isinstance(arg, dict) and any([is_pyobject_tainted(x) for x in arg.values()])) ): diff --git a/ddtrace/appsec/_iast/_taint_tracking/aspects.py b/ddtrace/appsec/_iast/_taint_tracking/aspects.py index e2577cd3048..3c21d1c7a33 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/aspects.py +++ b/ddtrace/appsec/_iast/_taint_tracking/aspects.py @@ -15,6 +15,7 @@ from typing import Text from typing import Tuple from typing import Union +import _io from ddtrace.appsec._constants import IAST @@ -94,9 +95,64 @@ "ospathsplitext_aspect", "ospathsplitdrive_aspect", "ospathsplitroot_aspect", + "bytesio_aspect", + "stringio_aspect", + "read_aspect", ] +def stringio_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: + if orig_function is not None: + if flag_added_args > 0: + args = args[flag_added_args:] + result = orig_function(*args, **kwargs) + else: + if flag_added_args > 0: + args = args[flag_added_args:] + result = _io.StringIO(*args, **kwargs) + + if args and is_pyobject_tainted(args[0]): + try: + copy_and_shift_ranges_from_strings(args[0], result, 0) + except Exception as e: + iast_taint_log_error("IAST propagation error. stringio_aspect. {}".format(e)) + return result + + +def bytesio_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: + if orig_function is not None: + if flag_added_args > 0: + args = args[flag_added_args:] + result = orig_function(*args, **kwargs) + else: + if flag_added_args > 0: + args = args[flag_added_args:] + result = _io.BytesIO(*args, **kwargs) + + if args and is_pyobject_tainted(args[0]): + try: + copy_and_shift_ranges_from_strings(args[0], result, 0) + except Exception as e: + iast_taint_log_error("IAST propagation error. bytesio_aspect. {}".format(e)) + return result + + +def read_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: + if orig_function is not None: + if flag_added_args > 0: + args = args[flag_added_args:] + result = orig_function(*args, **kwargs) + else: + result = args[0].read(*args[1:], **kwargs) + + if args and is_pyobject_tainted(args[0]): + try: + copy_and_shift_ranges_from_strings(args[0], result, 0) + except Exception as e: + iast_taint_log_error("IAST propagation error. read_aspect. {}".format(e)) + return result + + def str_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: if orig_function is not None: if orig_function != builtin_str: diff --git a/tests/appsec/app.py b/tests/appsec/app.py index b6a0b04824b..d7d70345dd0 100644 --- a/tests/appsec/app.py +++ b/tests/appsec/app.py @@ -200,6 +200,69 @@ def iast_ast_patching_import_error(): return Response(str(module_with_import_errors.verbal_kint_is_keyser_soze)) +@app.route("/iast-ast-patching-io-bytesio", methods=["GET"]) +def iast_ast_patching_io_bytes_io(): + filename = request.args.get("filename") + style = request.args.get("style") + bytes_filename = filename.encode() + if style == "_io_module": + import _io + + changed = _io.BytesIO(bytes_filename) + elif style == "io_module": + import io + + changed = io.BytesIO(bytes_filename) + elif style == "io_function": + from io import BytesIO + + changed = BytesIO(bytes_filename) + else: + from _io import BytesIO + + changed = BytesIO(bytes_filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if is_pyobject_tainted(changed): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + +@app.route("/iast-ast-patching-io-stringio", methods=["GET"]) +def iast_ast_patching_io_string_io(): + filename = request.args.get("filename") + style = request.args.get("style") + if style == "_io_module": + import _io + + changed = _io.StringIO(filename) + elif style == "io_module": + import io + + changed = io.StringIO(filename) + elif style == "io_function": + from io import StringIO + + changed = StringIO(filename) + else: + from _io import StringIO + + changed = StringIO(filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if is_pyobject_tainted(changed): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + @app.route("/iast-ast-patching-re-sub", methods=["GET"]) def iast_ast_patching_re_sub(): filename = request.args.get("filename") diff --git a/tests/appsec/iast_packages/test_packages.py b/tests/appsec/iast_packages/test_packages.py index 903270bf7ac..0a677cca8bb 100644 --- a/tests/appsec/iast_packages/test_packages.py +++ b/tests/appsec/iast_packages/test_packages.py @@ -439,7 +439,6 @@ def uninstall(self, python_cmd): "", import_module_to_validate="pyasn1.codec.native.decoder", test_propagation=True, - fixme_propagation_fails=True, ), PackageForTesting("pycparser", "2.22", "", "", ""), PackageForTesting( diff --git a/tests/appsec/integrations/test_flask_iast_patching.py b/tests/appsec/integrations/test_flask_iast_patching.py index c2caf98c16d..a253df27fbf 100644 --- a/tests/appsec/integrations/test_flask_iast_patching.py +++ b/tests/appsec/integrations/test_flask_iast_patching.py @@ -57,3 +57,27 @@ def test_flask_iast_ast_patching_re(style, endpoint, function): assert response.status_code == 200 assert response.content == b"OK" + + +@pytest.mark.parametrize("style", ["_io_module", "io_module", "io_function", "_io_function"]) +@pytest.mark.parametrize( + "function", + [ + "bytesio", + "stringio", + ], +) +def test_flask_iast_ast_patching_io(style, function, endpoint="io"): + """ + Tests _io/io BytesIO and StringIO patching end to end + """ + filename = "path_traversal_test_file.txt" + with flask_server( + appsec_enabled="false", iast_enabled="true", token=None, port=8020, assert_debug=False + ) as context: + _, flask_client, pid = context + + response = flask_client.get(f"/iast-ast-patching-{endpoint}-{function}?style={style}&filename={filename}") + + assert response.status_code == 200 + assert response.content == b"OK" From bfd74ac78df2c1871260e850be8a173597f582e5 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Tue, 17 Sep 2024 18:18:44 +0200 Subject: [PATCH 02/14] ruff --- ddtrace/appsec/_constants.py | 5 +++-- ddtrace/appsec/_iast/_taint_tracking/__init__.py | 1 - ddtrace/appsec/_iast/_taint_tracking/aspects.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ddtrace/appsec/_constants.py b/ddtrace/appsec/_constants.py index 568b98d9891..9054dc41882 100644 --- a/ddtrace/appsec/_constants.py +++ b/ddtrace/appsec/_constants.py @@ -1,9 +1,10 @@ import os -from _io import BytesIO -from _io import StringIO from re import Match import sys +from _io import BytesIO +from _io import StringIO + if sys.version_info >= (3, 8): from typing import Literal # noqa:F401 diff --git a/ddtrace/appsec/_iast/_taint_tracking/__init__.py b/ddtrace/appsec/_iast/_taint_tracking/__init__.py index 4ce158cfeaf..7b66357edc3 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/__init__.py +++ b/ddtrace/appsec/_iast/_taint_tracking/__init__.py @@ -1,6 +1,5 @@ from io import BytesIO from io import StringIO - import os from typing import Any from typing import Tuple diff --git a/ddtrace/appsec/_iast/_taint_tracking/aspects.py b/ddtrace/appsec/_iast/_taint_tracking/aspects.py index 3c21d1c7a33..e6162ae9838 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/aspects.py +++ b/ddtrace/appsec/_iast/_taint_tracking/aspects.py @@ -15,6 +15,7 @@ from typing import Text from typing import Tuple from typing import Union + import _io from ddtrace.appsec._constants import IAST From 1a0948147fea73ca311e79471754e50d61fde3b0 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Tue, 17 Sep 2024 18:30:59 +0200 Subject: [PATCH 03/14] revert as it isn't passing yet --- tests/appsec/iast_packages/test_packages.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/appsec/iast_packages/test_packages.py b/tests/appsec/iast_packages/test_packages.py index 0a677cca8bb..903270bf7ac 100644 --- a/tests/appsec/iast_packages/test_packages.py +++ b/tests/appsec/iast_packages/test_packages.py @@ -439,6 +439,7 @@ def uninstall(self, python_cmd): "", import_module_to_validate="pyasn1.codec.native.decoder", test_propagation=True, + fixme_propagation_fails=True, ), PackageForTesting("pycparser", "2.22", "", "", ""), PackageForTesting( From 8a97387d0380a3f0b3e46ed6477c7aa6006724c0 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Thu, 19 Sep 2024 13:28:23 +0200 Subject: [PATCH 04/14] check types --- ddtrace/appsec/_iast/_taint_tracking/aspects.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ddtrace/appsec/_iast/_taint_tracking/aspects.py b/ddtrace/appsec/_iast/_taint_tracking/aspects.py index e6162ae9838..2c7145e8a5e 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/aspects.py +++ b/ddtrace/appsec/_iast/_taint_tracking/aspects.py @@ -102,7 +102,7 @@ ] -def stringio_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: +def stringio_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> _io.StringIO: if orig_function is not None: if flag_added_args > 0: args = args[flag_added_args:] @@ -112,7 +112,7 @@ def stringio_aspect(orig_function: Optional[Callable], flag_added_args: int, *ar args = args[flag_added_args:] result = _io.StringIO(*args, **kwargs) - if args and is_pyobject_tainted(args[0]): + if args and is_pyobject_tainted(args[0]) and isinstance(result, _io.StringIO): try: copy_and_shift_ranges_from_strings(args[0], result, 0) except Exception as e: @@ -120,7 +120,7 @@ def stringio_aspect(orig_function: Optional[Callable], flag_added_args: int, *ar return result -def bytesio_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: +def bytesio_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> _io.BytesIO: if orig_function is not None: if flag_added_args > 0: args = args[flag_added_args:] @@ -130,7 +130,7 @@ def bytesio_aspect(orig_function: Optional[Callable], flag_added_args: int, *arg args = args[flag_added_args:] result = _io.BytesIO(*args, **kwargs) - if args and is_pyobject_tainted(args[0]): + if args and is_pyobject_tainted(args[0]) and isinstance(result, _io.BytesIO): try: copy_and_shift_ranges_from_strings(args[0], result, 0) except Exception as e: @@ -138,7 +138,9 @@ def bytesio_aspect(orig_function: Optional[Callable], flag_added_args: int, *arg return result -def read_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: +def read_aspect( + orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any +) -> Union[str, bytes]: if orig_function is not None: if flag_added_args > 0: args = args[flag_added_args:] @@ -146,7 +148,7 @@ def read_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: else: result = args[0].read(*args[1:], **kwargs) - if args and is_pyobject_tainted(args[0]): + if args and is_pyobject_tainted(args[0]) and isinstance(args[0], _io._IOBase): try: copy_and_shift_ranges_from_strings(args[0], result, 0) except Exception as e: From 9ce1c08f5b4f5b7ab73a9a686422f8abce8f27a2 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Thu, 19 Sep 2024 13:52:32 +0200 Subject: [PATCH 05/14] add some tests --- tests/appsec/iast/_ast/test_ast_patching.py | 49 +++++++++++++++++++ .../appsec/iast/fixtures/propagation_path.py | 4 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/appsec/iast/_ast/test_ast_patching.py b/tests/appsec/iast/_ast/test_ast_patching.py index 155a0270bc3..cdf4cb01b15 100644 --- a/tests/appsec/iast/_ast/test_ast_patching.py +++ b/tests/appsec/iast/_ast/test_ast_patching.py @@ -172,3 +172,52 @@ def test_module_path_none(caplog): with caplog.at_level(logging.DEBUG), mock.patch("ddtrace.internal.module.Path.resolve", side_effect=AttributeError): assert ("", "") == astpatch_module(__import__("tests.appsec.iast.fixtures.ast.str.class_str", fromlist=[None])) assert "astpatch_source couldn't find the module: tests.appsec.iast.fixtures.ast.str.class_str" in caplog.text + + +@pytest.mark.parametrize( + "module_name", + [ + ("tests.appsec.iast.fixtures.ast.io.module_stringio"), + ("tests.appsec.iast.fixtures.ast.io.function_stringio"), + ], +) +def test_astpatch_stringio_module_changed(module_name): + module_path, new_source = astpatch_module(__import__(module_name, fromlist=[None])) + assert ("", "") != (module_path, new_source) + new_code = astunparse.unparse(new_source) + assert new_code.startswith( + "\nimport ddtrace.appsec._iast.taint_sinks as ddtrace_taint_sinks" + "\nimport ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects" + ) + assert "ddtrace_aspects.stringio_aspect(" in new_code + + +@pytest.mark.parametrize( + "module_name", + [ + ("tests.appsec.iast.fixtures.ast.io.module_bytesio"), + ("tests.appsec.iast.fixtures.ast.io.function_bytesio"), + ], +) +def test_astpatch_bytesio_module_changed(module_name): + module_path, new_source = astpatch_module(__import__(module_name, fromlist=[None])) + assert ("", "") != (module_path, new_source) + new_code = astunparse.unparse(new_source) + assert new_code.startswith( + "\nimport ddtrace.appsec._iast.taint_sinks as ddtrace_taint_sinks" + "\nimport ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects" + ) + assert "ddtrace_aspects.bytesio_aspect(" in new_code + + +def test_astpatch_read_module_changed(): + module_path, new_source = astpatch_module( + __import__("tests.appsec.iast.fixtures.ast.io.module_read", fromlist=[None]) + ) + assert ("", "") != (module_path, new_source) + new_code = astunparse.unparse(new_source) + assert new_code.startswith( + "\nimport ddtrace.appsec._iast.taint_sinks as ddtrace_taint_sinks" + "\nimport ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects" + ) + assert "ddtrace_aspects.read_aspect(" in new_code diff --git a/tests/appsec/iast/fixtures/propagation_path.py b/tests/appsec/iast/fixtures/propagation_path.py index 2c46d2f2a69..fd39e6b23cf 100644 --- a/tests/appsec/iast/fixtures/propagation_path.py +++ b/tests/appsec/iast/fixtures/propagation_path.py @@ -181,4 +181,6 @@ def propagation_memory_check(origin_string1, tainted_string_2): _ = m.read() except Exception: pass - return string23 + import _io + + return _io.StringIO(string23).read() From 106171e0f72b78fde5b74cb0cf598fd775fb6cdf Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Thu, 19 Sep 2024 15:06:36 +0200 Subject: [PATCH 06/14] add new fixtures --- tests/appsec/iast/fixtures/ast/io/function_bytesio.py | 6 ++++++ tests/appsec/iast/fixtures/ast/io/function_stringio.py | 5 +++++ tests/appsec/iast/fixtures/ast/io/module_bytesio.py | 6 ++++++ tests/appsec/iast/fixtures/ast/io/module_read.py | 8 ++++++++ tests/appsec/iast/fixtures/ast/io/module_stringio.py | 5 +++++ 5 files changed, 30 insertions(+) create mode 100644 tests/appsec/iast/fixtures/ast/io/function_bytesio.py create mode 100644 tests/appsec/iast/fixtures/ast/io/function_stringio.py create mode 100644 tests/appsec/iast/fixtures/ast/io/module_bytesio.py create mode 100644 tests/appsec/iast/fixtures/ast/io/module_read.py create mode 100644 tests/appsec/iast/fixtures/ast/io/module_stringio.py diff --git a/tests/appsec/iast/fixtures/ast/io/function_bytesio.py b/tests/appsec/iast/fixtures/ast/io/function_bytesio.py new file mode 100644 index 00000000000..5ef6dc63e22 --- /dev/null +++ b/tests/appsec/iast/fixtures/ast/io/function_bytesio.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +from _io import BytesIO + + +def fixture_function_bytesio(): + return BytesIO(b"test") diff --git a/tests/appsec/iast/fixtures/ast/io/function_stringio.py b/tests/appsec/iast/fixtures/ast/io/function_stringio.py new file mode 100644 index 00000000000..eeafc804672 --- /dev/null +++ b/tests/appsec/iast/fixtures/ast/io/function_stringio.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from _io import StringIO + +def fixture_function_stringio(): + return StringIO("test") diff --git a/tests/appsec/iast/fixtures/ast/io/module_bytesio.py b/tests/appsec/iast/fixtures/ast/io/module_bytesio.py new file mode 100644 index 00000000000..7f5185e8920 --- /dev/null +++ b/tests/appsec/iast/fixtures/ast/io/module_bytesio.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import _io + + +def fixture_function_bytesio(): + return _io.BytesIO(b"test") diff --git a/tests/appsec/iast/fixtures/ast/io/module_read.py b/tests/appsec/iast/fixtures/ast/io/module_read.py new file mode 100644 index 00000000000..e128303c3aa --- /dev/null +++ b/tests/appsec/iast/fixtures/ast/io/module_read.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 + + +def read_from_io(my_object): + try: + my_object.read(5) + except AttributeError as e: + raise AttributeError("Object does not have a read method") from e diff --git a/tests/appsec/iast/fixtures/ast/io/module_stringio.py b/tests/appsec/iast/fixtures/ast/io/module_stringio.py new file mode 100644 index 00000000000..547cd90b6fb --- /dev/null +++ b/tests/appsec/iast/fixtures/ast/io/module_stringio.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +import _io + +def fixture_function_stringio(): + return _io.StringIO("test") From 2d76d3a2121280473b664228b0f8da6272494f68 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Thu, 19 Sep 2024 15:45:21 +0200 Subject: [PATCH 07/14] black --- tests/appsec/iast/fixtures/ast/io/function_stringio.py | 1 + tests/appsec/iast/fixtures/ast/io/module_stringio.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/appsec/iast/fixtures/ast/io/function_stringio.py b/tests/appsec/iast/fixtures/ast/io/function_stringio.py index eeafc804672..4b2f2fe6063 100644 --- a/tests/appsec/iast/fixtures/ast/io/function_stringio.py +++ b/tests/appsec/iast/fixtures/ast/io/function_stringio.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 from _io import StringIO + def fixture_function_stringio(): return StringIO("test") diff --git a/tests/appsec/iast/fixtures/ast/io/module_stringio.py b/tests/appsec/iast/fixtures/ast/io/module_stringio.py index 547cd90b6fb..493acebe596 100644 --- a/tests/appsec/iast/fixtures/ast/io/module_stringio.py +++ b/tests/appsec/iast/fixtures/ast/io/module_stringio.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import _io + def fixture_function_stringio(): return _io.StringIO("test") From e21d40af2e6b41716c7a9a8aa5187003a4975949 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Thu, 19 Sep 2024 18:05:33 +0200 Subject: [PATCH 08/14] change read aspect to monkeypatch --- ddtrace/appsec/_common_module_patches.py | 18 ++++++++++++++++++ ddtrace/appsec/_iast/_ast/visitor.py | 2 -- .../appsec/_iast/_taint_tracking/aspects.py | 19 ------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/ddtrace/appsec/_common_module_patches.py b/ddtrace/appsec/_common_module_patches.py index 71aa43db4c0..6e4159cf4de 100644 --- a/ddtrace/appsec/_common_module_patches.py +++ b/ddtrace/appsec/_common_module_patches.py @@ -30,6 +30,8 @@ def patch_common_modules(): try_wrap_function_wrapper("builtins", "open", wrapped_open_CFDDB7ABBA9081B6) try_wrap_function_wrapper("urllib.request", "OpenerDirector.open", wrapped_open_ED4CF71136E15EBF) + try_wrap_function_wrapper("_io", "BytesIO.read", wrapped_read_F3E51D71B4EC16EF) + try_wrap_function_wrapper("_io", "StringIO.read", wrapped_read_F3E51D71B4EC16EF) try_wrap_function_wrapper("os", "system", wrapped_system_5542593D237084A7) core.on("asm.block.dbapi.execute", execute_4C9BAC8E228EB347) if asm_config._iast_enabled: @@ -39,6 +41,22 @@ def patch_common_modules(): def unpatch_common_modules(): try_unwrap("builtins", "open") try_unwrap("urllib.request", "OpenerDirector.open") + try_unwrap("_io", "BytesIO.read") + try_unwrap("_io", "StringIO.read") + + +def wrapped_read_F3E51D71B4EC16EF(original_read_callable, instance, args, kwargs): + """ + wrapper for _io.BytesIO and _io.StringIO read function + """ + result = original_read_callable(*args, **kwargs) + if asm_config._iast_enabled: + from ddtrace.appsec._iast._taint_tracking import copy_and_shift_ranges_from_strings + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if is_pyobject_tainted(instance): + copy_and_shift_ranges_from_strings(instance, result, 0) + return result def wrapped_open_CFDDB7ABBA9081B6(original_open_callable, instance, args, kwargs): diff --git a/ddtrace/appsec/_iast/_ast/visitor.py b/ddtrace/appsec/_iast/_ast/visitor.py index 410111edf7b..b3f90a9046a 100644 --- a/ddtrace/appsec/_iast/_ast/visitor.py +++ b/ddtrace/appsec/_iast/_ast/visitor.py @@ -42,7 +42,6 @@ def _mark_avoid_convert_recursively(node): "functions": { "StringIO": "ddtrace_aspects.stringio_aspect", "BytesIO": "ddtrace_aspects.bytesio_aspect", - "read": "ddtrace_aspects.read_aspect", "str": "ddtrace_aspects.str_aspect", "bytes": "ddtrace_aspects.bytes_aspect", "bytearray": "ddtrace_aspects.bytearray_aspect", @@ -51,7 +50,6 @@ def _mark_avoid_convert_recursively(node): "stringalike_methods": { "StringIO": "ddtrace_aspects.stringio_aspect", "BytesIO": "ddtrace_aspects.bytesio_aspect", - "read": "ddtrace_aspects.read_aspect", "decode": "ddtrace_aspects.decode_aspect", "join": "ddtrace_aspects.join_aspect", "encode": "ddtrace_aspects.encode_aspect", diff --git a/ddtrace/appsec/_iast/_taint_tracking/aspects.py b/ddtrace/appsec/_iast/_taint_tracking/aspects.py index 2c7145e8a5e..2d55be3d65e 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/aspects.py +++ b/ddtrace/appsec/_iast/_taint_tracking/aspects.py @@ -98,7 +98,6 @@ "ospathsplitroot_aspect", "bytesio_aspect", "stringio_aspect", - "read_aspect", ] @@ -138,24 +137,6 @@ def bytesio_aspect(orig_function: Optional[Callable], flag_added_args: int, *arg return result -def read_aspect( - orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any -) -> Union[str, bytes]: - if orig_function is not None: - if flag_added_args > 0: - args = args[flag_added_args:] - result = orig_function(*args, **kwargs) - else: - result = args[0].read(*args[1:], **kwargs) - - if args and is_pyobject_tainted(args[0]) and isinstance(args[0], _io._IOBase): - try: - copy_and_shift_ranges_from_strings(args[0], result, 0) - except Exception as e: - iast_taint_log_error("IAST propagation error. read_aspect. {}".format(e)) - return result - - def str_aspect(orig_function: Optional[Callable], flag_added_args: int, *args: Any, **kwargs: Any) -> str: if orig_function is not None: if orig_function != builtin_str: From d3fa59ef12302f5be615310a3d395cf1accc7870 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Thu, 19 Sep 2024 18:08:04 +0200 Subject: [PATCH 09/14] remove read ast test --- tests/appsec/iast/_ast/test_ast_patching.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/appsec/iast/_ast/test_ast_patching.py b/tests/appsec/iast/_ast/test_ast_patching.py index cdf4cb01b15..a9092ef7839 100644 --- a/tests/appsec/iast/_ast/test_ast_patching.py +++ b/tests/appsec/iast/_ast/test_ast_patching.py @@ -208,16 +208,3 @@ def test_astpatch_bytesio_module_changed(module_name): "\nimport ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects" ) assert "ddtrace_aspects.bytesio_aspect(" in new_code - - -def test_astpatch_read_module_changed(): - module_path, new_source = astpatch_module( - __import__("tests.appsec.iast.fixtures.ast.io.module_read", fromlist=[None]) - ) - assert ("", "") != (module_path, new_source) - new_code = astunparse.unparse(new_source) - assert new_code.startswith( - "\nimport ddtrace.appsec._iast.taint_sinks as ddtrace_taint_sinks" - "\nimport ddtrace.appsec._iast._taint_tracking.aspects as ddtrace_aspects" - ) - assert "ddtrace_aspects.read_aspect(" in new_code From 2a3cb59f5d531cb75f79e22d7cb9691979c8c37e Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Mon, 23 Sep 2024 09:50:38 +0200 Subject: [PATCH 10/14] change PyIOBase_Check to new form --- .../_iast/_taint_tracking/Utils/StringUtils.cpp | 11 +++++++++++ .../appsec/_iast/_taint_tracking/Utils/StringUtils.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.cpp b/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.cpp index 03cc81d2684..a0462691441 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.cpp +++ b/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.cpp @@ -144,6 +144,17 @@ get_pyobject_size(PyObject* obj) return len_candidate_text; } +bool +PyIOBase_Check(const PyObject* obj) +{ + try { + return py::isinstance((PyObject*)obj, safe_import("_io", "_IOBase")); + } catch (py::error_already_set& err) { + PyErr_Clear(); + return false; + } +} + bool PyReMatch_Check(const PyObject* obj) { diff --git a/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h b/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h index b2570e6b872..d5c9dd59fa7 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h +++ b/ddtrace/appsec/_iast/_taint_tracking/Utils/StringUtils.h @@ -32,6 +32,8 @@ get_unique_id(const PyObject* str) return reinterpret_cast(str); } +bool +PyIOBase_Check(const PyObject* obj); bool PyReMatch_Check(const PyObject* obj); From 926a38517a3b15a07cd93c2acf5efb324e5114d3 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Mon, 23 Sep 2024 10:46:49 +0200 Subject: [PATCH 11/14] add tests and microbenchmarks --- benchmarks/appsec_iast_aspects/config.yaml | 90 +++++++++ benchmarks/bm/iast_fixtures/str_methods.py | 12 ++ tests/appsec/app.py | 189 ++++++++++++++++++ .../integrations/test_flask_iast_patching.py | 6 + 4 files changed, 297 insertions(+) diff --git a/benchmarks/appsec_iast_aspects/config.yaml b/benchmarks/appsec_iast_aspects/config.yaml index d4ccaa80023..c8eca9b8951 100644 --- a/benchmarks/appsec_iast_aspects/config.yaml +++ b/benchmarks/appsec_iast_aspects/config.yaml @@ -233,6 +233,42 @@ aspect_iast_do_index_on_dict: warmups: 1 iast_enabled: 1 +aspect_no_iast_do_io_bytesio_read: &aspect_no_iast_do_io_bytesio_read + iast_enabled: 0 + processes: 10 + loops: 1 + values: 6 + warmups: 1 + mod_original_name: "bm.iast_fixtures.str_methods" + function_name: "do_io_bytesio_read" + args: [' fOobaR\t \n'] + +aspect_iast_do_io_bytesio_read: + << : *aspect_no_iast_do_io_bytesio_read + processes: 10 + loops: 1 + values: 6 + warmups: 1 + iast_enabled: 1 + +aspect_no_iast_do_io_stringio_read: &aspect_no_iast_do_io_stringio_read + iast_enabled: 0 + processes: 10 + loops: 1 + values: 6 + warmups: 1 + mod_original_name: "bm.iast_fixtures.str_methods" + function_name: "do_io_stringio_read" + args: [' fOobaR\t \n'] + +aspect_iast_do_io_stringio_read: + << : *aspect_no_iast_do_io_stringio_read + processes: 10 + loops: 1 + values: 6 + warmups: 1 + iast_enabled: 1 + aspect_no_iast_do_join: &aspect_no_iast_do_join iast_enabled: 0 processes: 10 @@ -359,6 +395,60 @@ aspect_iast_do_namedtuple: warmups: 1 iast_enabled: 1 +aspect_no_iast_do_operator_add_inplace_3_params: &aspect_no_iast_do_operator_add_inplace_3_params + iast_enabled: 0 + processes: 10 + loops: 1 + values: 6 + warmups: 1 + mod_original_name: "bm.iast_fixtures.str_methods" + function_name: "do_operator_add_inplace_3_params" + args: [' fOobaR\t \n', ' fOobaR\t \n', ' fOobaR\t \n'] + +aspect_iast_do_operator_add_inplace_3_params: + << : *aspect_no_iast_do_operator_add_inplace_3_params + processes: 10 + loops: 1 + values: 6 + warmups: 1 + iast_enabled: 1 + +aspect_no_iast_do_operator_add_inplace_3_times: &aspect_no_iast_do_operator_add_inplace_3_times + iast_enabled: 0 + processes: 10 + loops: 1 + values: 6 + warmups: 1 + mod_original_name: "bm.iast_fixtures.str_methods" + function_name: "do_operator_add_inplace_3_times" + args: [' fOobaR\t \n', ' fOobaR\t \n'] + +aspect_iast_do_operator_add_inplace_3_times: + << : *aspect_no_iast_do_operator_add_inplace_3_times + processes: 10 + loops: 1 + values: 6 + warmups: 1 + iast_enabled: 1 + +aspect_no_iast_do_operator_add_inplace_params: &aspect_no_iast_do_operator_add_inplace_params + iast_enabled: 0 + processes: 10 + loops: 1 + values: 6 + warmups: 1 + mod_original_name: "bm.iast_fixtures.str_methods" + function_name: "do_operator_add_inplace_params" + args: [' fOobaR\t \n', ' fOobaR\t \n'] + +aspect_iast_do_operator_add_inplace_params: + << : *aspect_no_iast_do_operator_add_inplace_params + processes: 10 + loops: 1 + values: 6 + warmups: 1 + iast_enabled: 1 + aspect_no_iast_do_operator_add_params: &aspect_no_iast_do_operator_add_params iast_enabled: 0 processes: 10 diff --git a/benchmarks/bm/iast_fixtures/str_methods.py b/benchmarks/bm/iast_fixtures/str_methods.py index 79431775cc4..2f7e0b1d342 100644 --- a/benchmarks/bm/iast_fixtures/str_methods.py +++ b/benchmarks/bm/iast_fixtures/str_methods.py @@ -21,6 +21,8 @@ from typing import Tuple import urllib.parse +import _io + def methodcaller(*args, **kwargs): return "im methodcaller" @@ -1245,3 +1247,13 @@ def urlib_urlsplit(text): def do_re_match_index(text, regexp, index): match = re.search(regexp, text) return match[index] + + +def do_io_stringio_read(string_input): + xxx = _io.StringIO(string_input) + return xxx.read() + + +def do_io_bytesio_read(string_input): + xxx = _io.BytesIO(string_input.encode("utf-8")) + return xxx.read() diff --git a/tests/appsec/app.py b/tests/appsec/app.py index d7d70345dd0..f1496449406 100644 --- a/tests/appsec/app.py +++ b/tests/appsec/app.py @@ -200,6 +200,132 @@ def iast_ast_patching_import_error(): return Response(str(module_with_import_errors.verbal_kint_is_keyser_soze)) +@app.route("/iast-ast-patching-io-bytesio-untainted", methods=["GET"]) +def iast_ast_patching_io_bytes_io_untainted(): + filename = "filename" + style = request.args.get("style") + bytes_filename = filename.encode() + if style == "_io_module": + import _io + + changed = _io.BytesIO(bytes_filename) + elif style == "io_module": + import io + + changed = io.BytesIO(bytes_filename) + elif style == "io_function": + from io import BytesIO + + changed = BytesIO(bytes_filename) + else: + from _io import BytesIO + + changed = BytesIO(bytes_filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if not is_pyobject_tainted(changed): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + +@app.route("/iast-ast-patching-io-stringio-untainted", methods=["GET"]) +def iast_ast_patching_io_string_io_untainted(): + filename = "filename" + style = request.args.get("style") + if style == "_io_module": + import _io + + changed = _io.StringIO(filename) + elif style == "io_module": + import io + + changed = io.StringIO(filename) + elif style == "io_function": + from io import StringIO + + changed = StringIO(filename) + else: + from _io import StringIO + + changed = StringIO(filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if not is_pyobject_tainted(changed): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + +@app.route("/iast-ast-patching-io-bytesio-read-untainted", methods=["GET"]) +def iast_ast_patching_io_bytes_io_read_untainted(): + filename = "filename" + style = request.args.get("style") + bytes_filename = filename.encode() + if style == "_io_module": + import _io + + changed = _io.BytesIO(bytes_filename) + elif style == "io_module": + import io + + changed = io.BytesIO(bytes_filename) + elif style == "io_function": + from io import BytesIO + + changed = BytesIO(bytes_filename) + else: + from _io import BytesIO + + changed = BytesIO(bytes_filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if not is_pyobject_tainted(changed.read(4)): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + +@app.route("/iast-ast-patching-io-stringio-read-untainted", methods=["GET"]) +def iast_ast_patching_io_string_io_read_untainted(): + filename = "filename" + style = request.args.get("style") + if style == "_io_module": + import _io + + changed = _io.StringIO(filename) + elif style == "io_module": + import io + + changed = io.StringIO(filename) + elif style == "io_function": + from io import StringIO + + changed = StringIO(filename) + else: + from _io import StringIO + + changed = StringIO(filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if not is_pyobject_tainted(changed.read(4)): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + @app.route("/iast-ast-patching-io-bytesio", methods=["GET"]) def iast_ast_patching_io_bytes_io(): filename = request.args.get("filename") @@ -263,6 +389,69 @@ def iast_ast_patching_io_string_io(): return resp +@app.route("/iast-ast-patching-io-bytesio-read", methods=["GET"]) +def iast_ast_patching_io_bytes_io_read(): + filename = request.args.get("filename") + style = request.args.get("style") + bytes_filename = filename.encode() + if style == "_io_module": + import _io + + changed = _io.BytesIO(bytes_filename) + elif style == "io_module": + import io + + changed = io.BytesIO(bytes_filename) + elif style == "io_function": + from io import BytesIO + + changed = BytesIO(bytes_filename) + else: + from _io import BytesIO + + changed = BytesIO(bytes_filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if is_pyobject_tainted(changed.read(4)): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + +@app.route("/iast-ast-patching-io-stringio-read", methods=["GET"]) +def iast_ast_patching_io_string_io_read(): + filename = request.args.get("filename") + style = request.args.get("style") + if style == "_io_module": + import _io + + changed = _io.StringIO(filename) + elif style == "io_module": + import io + + changed = io.StringIO(filename) + elif style == "io_function": + from io import StringIO + + changed = StringIO(filename) + else: + from _io import StringIO + + changed = StringIO(filename) + resp = Response("Fail") + try: + from ddtrace.appsec._iast._taint_tracking import is_pyobject_tainted + + if is_pyobject_tainted(changed.read(4)): + resp = Response("OK") + except Exception as e: + print(e) + return resp + + @app.route("/iast-ast-patching-re-sub", methods=["GET"]) def iast_ast_patching_re_sub(): filename = request.args.get("filename") diff --git a/tests/appsec/integrations/test_flask_iast_patching.py b/tests/appsec/integrations/test_flask_iast_patching.py index a253df27fbf..3cb89ff6803 100644 --- a/tests/appsec/integrations/test_flask_iast_patching.py +++ b/tests/appsec/integrations/test_flask_iast_patching.py @@ -65,6 +65,12 @@ def test_flask_iast_ast_patching_re(style, endpoint, function): [ "bytesio", "stringio", + "bytesio-read", + "stringio-read", + "bytesio-untainted", + "stringio-untainted", + "bytesio-read-untainted", + "stringio-read-untainted", ], ) def test_flask_iast_ast_patching_io(style, function, endpoint="io"): From 92814f70b3b4441f9094a868859820ff1abf01da Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Mon, 23 Sep 2024 10:51:11 +0200 Subject: [PATCH 12/14] Partially Revert "add tests and microbenchmarks" This reverts commit 926a38517a3b15a07cd93c2acf5efb324e5114d3. --- benchmarks/appsec_iast_aspects/config.yaml | 54 ---------------------- 1 file changed, 54 deletions(-) diff --git a/benchmarks/appsec_iast_aspects/config.yaml b/benchmarks/appsec_iast_aspects/config.yaml index c8eca9b8951..ae59c47069b 100644 --- a/benchmarks/appsec_iast_aspects/config.yaml +++ b/benchmarks/appsec_iast_aspects/config.yaml @@ -395,60 +395,6 @@ aspect_iast_do_namedtuple: warmups: 1 iast_enabled: 1 -aspect_no_iast_do_operator_add_inplace_3_params: &aspect_no_iast_do_operator_add_inplace_3_params - iast_enabled: 0 - processes: 10 - loops: 1 - values: 6 - warmups: 1 - mod_original_name: "bm.iast_fixtures.str_methods" - function_name: "do_operator_add_inplace_3_params" - args: [' fOobaR\t \n', ' fOobaR\t \n', ' fOobaR\t \n'] - -aspect_iast_do_operator_add_inplace_3_params: - << : *aspect_no_iast_do_operator_add_inplace_3_params - processes: 10 - loops: 1 - values: 6 - warmups: 1 - iast_enabled: 1 - -aspect_no_iast_do_operator_add_inplace_3_times: &aspect_no_iast_do_operator_add_inplace_3_times - iast_enabled: 0 - processes: 10 - loops: 1 - values: 6 - warmups: 1 - mod_original_name: "bm.iast_fixtures.str_methods" - function_name: "do_operator_add_inplace_3_times" - args: [' fOobaR\t \n', ' fOobaR\t \n'] - -aspect_iast_do_operator_add_inplace_3_times: - << : *aspect_no_iast_do_operator_add_inplace_3_times - processes: 10 - loops: 1 - values: 6 - warmups: 1 - iast_enabled: 1 - -aspect_no_iast_do_operator_add_inplace_params: &aspect_no_iast_do_operator_add_inplace_params - iast_enabled: 0 - processes: 10 - loops: 1 - values: 6 - warmups: 1 - mod_original_name: "bm.iast_fixtures.str_methods" - function_name: "do_operator_add_inplace_params" - args: [' fOobaR\t \n', ' fOobaR\t \n'] - -aspect_iast_do_operator_add_inplace_params: - << : *aspect_no_iast_do_operator_add_inplace_params - processes: 10 - loops: 1 - values: 6 - warmups: 1 - iast_enabled: 1 - aspect_no_iast_do_operator_add_params: &aspect_no_iast_do_operator_add_params iast_enabled: 0 processes: 10 From 6a5f00c88a25872bbd3a6ea2ae5a123b13d794d6 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Mon, 23 Sep 2024 11:37:02 +0200 Subject: [PATCH 13/14] move _io import to top level --- tests/appsec/iast/fixtures/propagation_path.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/appsec/iast/fixtures/propagation_path.py b/tests/appsec/iast/fixtures/propagation_path.py index c89d61aca79..62cbc351ce7 100644 --- a/tests/appsec/iast/fixtures/propagation_path.py +++ b/tests/appsec/iast/fixtures/propagation_path.py @@ -6,6 +6,8 @@ import re import sys +import _io + ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -207,6 +209,5 @@ def propagation_memory_check(origin_string1, tainted_string_2): _ = m.read() except Exception: pass - import _io return _io.StringIO(string29).read() From d2f4968068ba07f36c7c26550e61afce499febb7 Mon Sep 17 00:00:00 2001 From: Federico Mon Date: Mon, 23 Sep 2024 12:30:06 +0200 Subject: [PATCH 14/14] increase mem leaks from 8.3 to 8.8 KB --- tests/appsec/iast_memcheck/test_iast_mem_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/appsec/iast_memcheck/test_iast_mem_check.py b/tests/appsec/iast_memcheck/test_iast_mem_check.py index 866075d8af2..f72d3d264aa 100644 --- a/tests/appsec/iast_memcheck/test_iast_mem_check.py +++ b/tests/appsec/iast_memcheck/test_iast_mem_check.py @@ -48,7 +48,7 @@ def __call__(self, stack: Stack) -> bool: return False -@pytest.mark.limit_leaks("8.3 KB", filter_fn=IASTFilter()) +@pytest.mark.limit_leaks("8.8 KB", filter_fn=IASTFilter()) @pytest.mark.parametrize( "origin1, origin2", [