feat(iast): enable Code Security on Python 3.15 - #19698
Conversation
Lifts the IAST version gate in ASMConfig from < 3.15 to < 3.16 so DD_IAST_ENABLED takes effect on Python 3.15 instead of being silently ignored. No build change is needed: the IAST native extensions are gated on platform only, never on Python version. Validated on Python 3.15.0rc1+dev: both native extensions build for cp315, the native taint-tracking gtest suite passes 186/186 against real 3.15 headers, and tests/appsec/iast/ is green (19,625 passed). No regression on 3.13 or 3.14. Two bugs surfaced during validation and are fixed here: - The code-injection aspect resolved the wrong caller scope, raising NameError in application code that calls eval(). It assumed a fixed inspect.currentframe().f_back depth, which only holds while wrapt uses its C extension. wrapt has no cp315 wheel, so 3.15 falls back to the pure-Python FunctionWrapper, which adds a frame. This is pre-existing rather than 3.15-specific: it reproduces on 3.14 with WRAPT_DISABLE_EXTENSIONS=1. - CPython 3.15 dropped the "or None" clause from its slice TypeError, so test_slice_aspect_fixtures.py asserted a message that no longer exists. IAST on 3.15 stays unreachable until the tracer wrapping work (#17849) and the requires-python / riot matrix bumps land, so this is correct but inert until then. Closes #17843 APPSEC-69649 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Circular import analysis
|
Codeowners resolved asResolved from the full PR diff against |
Dependency direction analysis
|
BenchmarksBenchmark execution time: 2026-08-14 12:51:18 Comparing candidate commit e7ac64a in PR branch Found 0 performance improvements and 7 performance regressions! Performance is the same for 612 metrics, 10 unstable metrics. scenario:httppropagationinject-ids_only
scenario:iastaspects-lower_aspect
scenario:iastaspects-title_aspect
scenario:iastaspectsospath-ospathbasename_aspect
scenario:span-start
scenario:telemetryaddmetric-1-count-metric-1-times
scenario:tracer-small
|
Description
Part of the Python 3.15 integration parity effort (parent tracker: #17809).
Closes #17843
APPSEC-69649
This PR enables IAST (Code Security) on Python 3.15.
There was no upstream blocker to bump. IAST's native extensions were never version-gated —
setup.pyadds_iast._ast.iastpatchand_iast._taint_tracking._nativeunder a platform-onlyguard — so the only thing disabling Code Security on 3.15 was the runtime tuple in
ASMConfig._iast_supported. This widens it from< (3, 15, 0)to< (3, 16, 0)and corrects theadjacent comment, which already said "3.6 to 3.13" while the code allowed 3.14.
Validating that gate surfaced two real bugs, both fixed here (see Bugs found below). The
eval()one matters: without it, turning IAST on for 3.15 would raiseNameErrorinside customerapplication code.
Changes
ddtrace/internal/settings/asm.py— widen_iast_supportedto 3.15, fix the stale comment, addan
AIDEV-NOTErecording that this is the only version gate and that the native extensions haveno build-time gate.
ddtrace/appsec/_iast/taint_sinks/code_injection.py— add_resolve_caller_frame()so thecode-injection aspect walks past
wrapt's wrapper frame when locating the caller ofeval().tests/appsec/iast/aspects/test_slice_aspect_fixtures.py— accept CPython 3.15's reworded sliceTypeError.featuresfor 3.15 support,fixesfor theeval()bug).Bugs found during validation
1.
eval()raisedNameErrorin application code whenever wrapt's C extension is absent._iast_coiresolved its caller with a fixedinspect.currentframe().f_back. That only lands on theuser's frame while wrapt uses its C
FunctionWrapper, which creates no Python frame. wrapt ships nocp315 wheel, so 3.15 falls back to the pure-Python implementation, which does add a frame — the
aspect then passed wrapt's globals/locals to
eval(), so a lambda default likeeval("lambda v,fun=fun: not fun(v)")could not seefun.This is pre-existing, not 3.15-specific. It reproduces on 3.14:
2. CPython 3.15 reworded its slice error message, dropping the
or Noneclause(
slice indices must be integers or None or have an __index__ method→slice indices must be integers or have an __index__ method). The aspect raises correctly; only thetest's asserted substring was stale.
Checklist
Adapted — several template items don't apply, because the blocker here was a runtime version check
rather than an upstream package pin:
Bumped upstream pin in— N/A, no upstream dependency involved.riotfile.pyLifted— N/A, the cap was amax_versioncap on the affected venv(s)sys.version_infocheck, not a riot cap.Ran— N/A,riot generateand committed lockfilesriotfile.pyis unchanged. TheIAST venvs already use
select_pys(), so they pick up 3.15 automatically once it joinsSUPPORTED_PYTHON_VERSIONS.scripts/run-tests; see the caveat there.Updated— N/A, that file tracks per-integration packagesupported_versions.jsonversions and has no IAST row.
releasenotes/notes/.Testing
Built CPython 3.15.0rc1+dev via
pyenv install 3.15-dev(matching.python-versionand.gitlab/testrunner.yml), stacked #17849 in a scratch worktree to get past theimport ddtraceblocker, and locally bumped
requires-pythonsopip install -e .would run. Neither of thoselocal-only edits is in this PR.
pip install -e .on 3.15_native.cpython-315*.soandiastpatch.cpython-315*.soboth producedappsec_iast_native)tests/appsec/iast/tests/appsec/iast_tdd_propagation/>= (3, 14)skips)appsec_iast_defaulton 3.14 (riot)appsec_iast_defaulton 3.13 (riot)CMake was confirmed to resolve the right interpreter (3.15 headers +
libpython3.15.so) rather thansilently picking the system Python.
Notable positives:
test_template_string_aspect.py(PEP-750 t-strings, 3.14+) collects andpasses on 3.15, exercising the
PY_VERSION_HEX >= 0x030E0000branch ofutils/string_utils.cpp; andtest_native_taint_range.py's refcount assertions hold under 3.15.Why raw
pytestfor the 3.15 leg.AGENTS.mdsays never to invoke pytest directly, but riotcannot target 3.15 at all today:
riotfile.py:SUPPORTED_PYTHON_VERSIONSandscripts/gen_gitlab_config.py:ALL_PYTHON_VERSIONSboth stop at 3.14, and there are zero 3.15lockfiles. The
env=/pkgs=blocks were replicated by hand from theappsec_iast_defaultvenv.Both 3.13/3.14 regression runs went through
scripts/run-testsas normal. Excluded on 3.15:test_grpc_iast.py(grpcio has no cp315 wheel).Risks
import ddtracestill raisesNotImplementedErrorfromddtrace/internal/wrapping/context.py([3.15] Tracer wrapping 3.15 #17810 / chore: wrapping context support for Python 3.15 #17849), andpyproject.tomlstill capsrequires-pythonat<3.15([3.15] AddProgramming Language :: Python :: 3.15classifier topyproject.toml#17815). The gate can only widen, sothere is no risk to existing users on 3.9–3.14.
the manual run above until 3.15 joins the riot matrix ([3.15] cp315 smoke tests (
tests/smoke_test.py,tests/lib-injection) green → flip cp315 wheel job fromallow-failto required #17816).< (3, 16, 0)optimistically covers all of 3.15.eval()fix changes frame resolution on all Python versions, not just 3.15. It is ano-op wherever wrapt's C extension is present (the common case), and 3.13/3.14 regression runs
are green.
Additional Notes
Findings handed to the Python 3.15 migration owners — all outside this PR's scope:
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1breaks the 3.15 build. It forces limited-API mode,which excludes
PyContextVar_New/Get/Set, sosrc/native/contextvar.rsfails withE0425.pyo3 0.28 supports 3.15 natively, so the flag is counterproductive there.
tests/conftest.pyblocks every test on 3.15. The autouseenable_crashtrackingfixtureasserts
crashtracking.is_started(), butsetup.pydrops the crashtracker Rust feature on 3.15,so
is_availableisFalseandstart()returns early. Needsyield platform.system() == "Linux" and crashtracking.is_available.pip<25cannot be satisfied on 3.15.appsec_iast_defaultpins it to work around IASTfirst-party detection, but pip 24.x fails to import on 3.15 (
typing.no_type_check_decoratorwasremoved). Resolving the
iastpatch.cTODO is a prerequisite for that suite on 3.15.ddtrace/internal/coverage/import_instrumentation_py3_12.pyimportsINJECTION_ASSEMBLY, which no longer exists inddtrace.internal.bytecode_injection. This breaksddtrace's own pytest plugin on all Python versions, not just 3.15.
python-314-f80c8356eaaf8392.yaml,more-314-suites-bf99a7d7ef1b2128.yaml)still list IAST as not working on 3.14, which was already inaccurate before this PR. Left
untouched, since both shipped in v3.16.0rc1.
🤖 Generated with Claude Code