Skip to content

Commit dd2c178

Browse files
authored
Merge branch 'main' into wantsui/add-component-telemetry
2 parents b3c6266 + 0a2688a commit dd2c178

File tree

56 files changed

+2282
-556
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2282
-556
lines changed

.github/workflows/system-tests.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
persist-credentials: false
2222
repository: 'DataDog/system-tests'
2323
# Automatically managed, use scripts/update-system-tests-version to update
24-
ref: 'bc5ac8df80c981de47ad258e066c26e2fc5ff4ee'
24+
ref: '3b3df04b753484a73a4b78fcdd95d5076da87602'
2525

2626
- name: Build agent
2727
run: ./build.sh -i agent
@@ -69,7 +69,7 @@ jobs:
6969
persist-credentials: false
7070
repository: 'DataDog/system-tests'
7171
# Automatically managed, use scripts/update-system-tests-version to update
72-
ref: 'bc5ac8df80c981de47ad258e066c26e2fc5ff4ee'
72+
ref: '3b3df04b753484a73a4b78fcdd95d5076da87602'
7373

7474
- name: Checkout dd-trace-py
7575
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
@@ -123,7 +123,7 @@ jobs:
123123
persist-credentials: false
124124
repository: 'DataDog/system-tests'
125125
# Automatically managed, use scripts/update-system-tests-version to update
126-
ref: 'bc5ac8df80c981de47ad258e066c26e2fc5ff4ee'
126+
ref: '3b3df04b753484a73a4b78fcdd95d5076da87602'
127127

128128
- name: Build runner
129129
uses: ./.github/actions/install_runner
@@ -310,7 +310,7 @@ jobs:
310310
persist-credentials: false
311311
repository: 'DataDog/system-tests'
312312
# Automatically managed, use scripts/update-system-tests-version to update
313-
ref: 'bc5ac8df80c981de47ad258e066c26e2fc5ff4ee'
313+
ref: '3b3df04b753484a73a4b78fcdd95d5076da87602'
314314
- name: Checkout dd-trace-py
315315
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
316316
with:

.riot/requirements/14676df.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.10
3+
# by the following command:
4+
#
5+
# pip-compile --no-annotate .riot/requirements/14676df.in
6+
#
7+
attrs==25.3.0
8+
coverage[toml]==7.8.2
9+
exceptiongroup==1.3.0
10+
freezegun==1.5.2
11+
hypothesis==6.45.0
12+
iniconfig==2.1.0
13+
mock==5.2.0
14+
opentracing==2.4.0
15+
packaging==25.0
16+
pluggy==1.6.0
17+
pygments==2.19.1
18+
pytest==8.4.0
19+
pytest-cov==6.1.1
20+
pytest-mock==3.14.1
21+
pytest-randomly==3.16.0
22+
python-dateutil==2.9.0.post0
23+
six==1.17.0
24+
sortedcontainers==2.4.0
25+
tomli==2.2.1
26+
typing-extensions==4.14.0

.riot/requirements/15de642.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.12
3+
# by the following command:
4+
#
5+
# pip-compile --no-annotate .riot/requirements/15de642.in
6+
#
7+
attrs==25.3.0
8+
coverage[toml]==7.8.2
9+
freezegun==1.5.2
10+
hypothesis==6.45.0
11+
iniconfig==2.1.0
12+
mock==5.2.0
13+
opentracing==2.4.0
14+
packaging==25.0
15+
pluggy==1.6.0
16+
pygments==2.19.1
17+
pytest==8.4.0
18+
pytest-cov==6.1.1
19+
pytest-mock==3.14.1
20+
pytest-randomly==3.16.0
21+
python-dateutil==2.9.0.post0
22+
six==1.17.0
23+
sortedcontainers==2.4.0

.riot/requirements/1d1dbc1.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.10
3+
# by the following command:
4+
#
5+
# pip-compile --no-annotate .riot/requirements/1d1dbc1.in
6+
#
7+
attrs==25.3.0
8+
coverage[toml]==7.8.2
9+
exceptiongroup==1.3.0
10+
freezegun==1.3.1
11+
hypothesis==6.45.0
12+
iniconfig==2.1.0
13+
mock==5.2.0
14+
opentracing==2.4.0
15+
packaging==25.0
16+
pluggy==1.6.0
17+
pygments==2.19.1
18+
pytest==8.4.0
19+
pytest-cov==6.1.1
20+
pytest-mock==3.14.1
21+
pytest-randomly==3.16.0
22+
python-dateutil==2.9.0.post0
23+
six==1.17.0
24+
sortedcontainers==2.4.0
25+
tomli==2.2.1
26+
typing-extensions==4.14.0

.riot/requirements/2bcce4e.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.12
3+
# by the following command:
4+
#
5+
# pip-compile --no-annotate .riot/requirements/2bcce4e.in
6+
#
7+
attrs==25.3.0
8+
coverage[toml]==7.8.2
9+
freezegun==1.3.1
10+
hypothesis==6.45.0
11+
iniconfig==2.1.0
12+
mock==5.2.0
13+
opentracing==2.4.0
14+
packaging==25.0
15+
pluggy==1.6.0
16+
pygments==2.19.1
17+
pytest==8.4.0
18+
pytest-cov==6.1.1
19+
pytest-mock==3.14.1
20+
pytest-randomly==3.16.0
21+
python-dateutil==2.9.0.post0
22+
six==1.17.0
23+
sortedcontainers==2.4.0

ddtrace/appsec/_api_security/api_manager.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ class TooLargeSchemaException(Exception):
3333

3434

3535
class APIManager(Service):
36-
COLLECTED = [
36+
BLOCK_COLLECTED = [
3737
("REQUEST_HEADERS_NO_COOKIES", API_SECURITY.REQUEST_HEADERS_NO_COOKIES, dict),
3838
("REQUEST_COOKIES", API_SECURITY.REQUEST_COOKIES, dict),
3939
("REQUEST_QUERY", API_SECURITY.REQUEST_QUERY, dict),
4040
("REQUEST_PATH_PARAMS", API_SECURITY.REQUEST_PATH_PARAMS, dict),
4141
("REQUEST_BODY", API_SECURITY.REQUEST_BODY, None),
42+
]
43+
COLLECTED = BLOCK_COLLECTED + [
4244
("RESPONSE_HEADERS_NO_COOKIES", API_SECURITY.RESPONSE_HEADERS_NO_COOKIES, dict),
4345
("RESPONSE_BODY", API_SECURITY.RESPONSE_BODY, lambda f: f()),
4446
]
@@ -132,7 +134,8 @@ def _schema_callback(self, env):
132134
if env.span is None or not asm_config._api_security_feature_active:
133135
return
134136
root = env.span._local_root or env.span
135-
if not root or any(meta_name in root._meta for _, meta_name, _ in self.COLLECTED):
137+
collected = self.BLOCK_COLLECTED if env.blocked else self.COLLECTED
138+
if not root or any(meta_name in root._meta for _, meta_name, _ in collected):
136139
return
137140

138141
try:
@@ -161,7 +164,7 @@ def _schema_callback(self, env):
161164
return
162165

163166
waf_payload = {"PROCESSOR_SETTINGS": {"extract-schema": True}}
164-
for address, _, transform in self.COLLECTED:
167+
for address, _, transform in collected:
165168
if not asm_config._api_security_parse_response_body and address == "RESPONSE_BODY":
166169
continue
167170
value = env.waf_addresses.get(SPAN_DATA_NAMES[address], _sentinel)

ddtrace/appsec/_iast/_ast/iastpatch.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ static size_t cached_packages_count = 0;
1818

1919
/* Static Lists */
2020
static const char* static_allowlist[] = {
21-
"jinja2.", "pygments.", "multipart.", "sqlalchemy.", "python_multipart.", "attrs.", "jsonschema.",
22-
"s3fs.", "mysql.", "pymysql.", "markupsafe.", "werkzeug.utils.", "langchain.", "langchain_core."
21+
"jinja2.", "pygments.", "multipart.", "sqlalchemy.", "python_multipart.",
22+
"attrs.", "jsonschema.", "s3fs.", "mysql.", "pymysql.",
23+
"markupsafe.", "werkzeug.utils.", "langchain.", "langchain_core.", "django.http.response"
2324
};
2425
static const size_t static_allowlist_count = sizeof(static_allowlist) / sizeof(static_allowlist[0]);
2526

ddtrace/appsec/_iast/_handlers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,12 @@ def _on_django_func_wrapped(fn_args, fn_kwargs, first_arg_expected_type, *_):
204204
)
205205
except AttributeError:
206206
log.debug("IAST can't set attribute http_req._body", exc_info=True)
207+
# This condition is only for testing purposes.
208+
# In real applications, http_req.body is typically a property that can be set.
209+
# Here we check if it's not a property to handle test cases where body is directly assigned.
207210
elif (
208211
getattr(http_req, "body", None) is not None
212+
and not isinstance(getattr(http_req, "body", None), property)
209213
and len(getattr(http_req, "body", None)) > 0
210214
and not is_pyobject_tainted(getattr(http_req, "body", None))
211215
):

ddtrace/appsec/_iast/_patch_modules.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ddtrace.appsec._iast.secure_marks.sanitizers import path_traversal_sanitizer
77
from ddtrace.appsec._iast.secure_marks.sanitizers import sqli_sanitizer
88
from ddtrace.appsec._iast.secure_marks.sanitizers import xss_sanitizer
9+
from ddtrace.appsec._iast.secure_marks.validators import header_injection_validator
910
from ddtrace.appsec._iast.secure_marks.validators import ssrf_validator
1011
from ddtrace.appsec._iast.secure_marks.validators import unvalidated_redirect_validator
1112

@@ -70,6 +71,20 @@ def patch_iast(patch_modules=IAST_PATCH):
7071
)
7172
)
7273

74+
# Header Injection validators
75+
# Header injection for > Django 3.2
76+
when_imported("django.http.response")(
77+
lambda _: try_wrap_function_wrapper(
78+
"django.http.response", "ResponseHeaders._convert_to_charset", header_injection_validator
79+
)
80+
)
81+
# Header injection for <= Django 2.2
82+
when_imported("django.http.response")(
83+
lambda _: try_wrap_function_wrapper(
84+
"django.http.response", "HttpResponseBase._convert_to_charset", header_injection_validator
85+
)
86+
)
87+
7388
# Unvalidated Redirect validators
7489
when_imported("django.utils.http")(
7590
lambda _: try_wrap_function_wrapper(

ddtrace/appsec/_iast/_stacktrace.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ _is_ddtrace_filename(const char* filename)
144144
static inline bool
145145
_is_site_packages_filename(const char* filename)
146146
{
147-
const bool res = filename && PURELIB_PATH && strncmp(filename, PURELIB_PATH, PURELIB_PATH_LEN) == 0;
147+
const bool res = filename && PURELIB_PATH &&
148+
(strstr(filename, "site-packages/") || strncmp(filename, PURELIB_PATH, PURELIB_PATH_LEN) == 0);
148149
return res;
149150
}
150151

ddtrace/appsec/_iast/secure_marks/validators.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ def unvalidated_redirect_validator(wrapped: Callable, instance: Any, args: Seque
9595
return create_validator(VulnerabilityType.UNVALIDATED_REDIRECT, wrapped, instance, args, kwargs)
9696

9797

98+
def header_injection_validator(wrapped: Callable, instance: Any, args: Sequence, kwargs: dict) -> bool:
99+
return create_validator(VulnerabilityType.HEADER_INJECTION, wrapped, instance, args, kwargs)
100+
101+
98102
def ssrf_validator(wrapped: Callable, instance: Any, args: Sequence, kwargs: dict) -> bool:
99103
"""Validator for ssrf functions.
100104

0 commit comments

Comments
 (0)