Skip to content

Commit 4626db4

Browse files
authored
fix(iast): format aspect propagation error (#7884)
IAST: Fix propagation error on ``.format`` string method. Error partially introduced in: #7772 ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [x] 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) - [x] If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. - [x] This PR doesn't touch any of that.
1 parent aa32be7 commit 4626db4

File tree

4 files changed

+27
-36
lines changed

4 files changed

+27
-36
lines changed

ddtrace/appsec/_iast/_taint_tracking/Aspects/AspectFormat.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,23 @@ api_format_aspect(StrType& candidate_text,
1717
py::list new_args;
1818
py::dict new_kwargs;
1919
for (const auto arg : args) {
20-
auto str_arg = py::cast<py::str>(arg);
21-
auto n_arg = _all_as_formatted_evidence<py::str>(str_arg, TagMappingMode::Mapper);
22-
new_args.append(n_arg);
20+
if (is_text(arg.ptr())) {
21+
auto str_arg = py::cast<py::str>(arg);
22+
auto n_arg = _all_as_formatted_evidence<py::str>(str_arg, TagMappingMode::Mapper);
23+
new_args.append(n_arg);
24+
} else {
25+
new_args.append(arg);
26+
}
2327
}
24-
for (auto [key, value] : new_kwargs) {
25-
auto str_value = py::cast<py::str>(value);
26-
auto n_value = _all_as_formatted_evidence<py::str>(str_value, TagMappingMode::Mapper);
27-
new_kwargs[key] = n_value;
28+
for (auto [key, value] : kwargs) {
29+
if (is_text(value.ptr())) {
30+
auto str_value = py::cast<py::str>(value);
31+
auto n_value = _all_as_formatted_evidence<py::str>(str_value, TagMappingMode::Mapper);
32+
new_kwargs[key] = n_value;
33+
} else {
34+
new_kwargs[key] = value;
35+
}
2836
}
29-
3037
StrType new_template_format =
3138
py::getattr(new_template, "format")(*(py::cast<py::tuple>(new_args)), **new_kwargs);
3239
std::tuple result = _convert_escaped_text_to_taint_text<StrType>(new_template_format, ranges_orig);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
Vulnerability Management for Code-level (IAST): Fix propagation error on ``.format`` string method.

tests/appsec/iast/aspects/test_format_aspect_fixtures.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ def test_format_when_tainted_unicode_emoji_strings_then_tainted_result(self):
109109
escaped_expected_result=":+-<input1>template⚠️<input1>-+: " ":+-<input2>parameter⚠️<input2>-+:",
110110
)
111111

112-
@pytest.mark.skip(reason="Migrating format_aspect to C++ breaks this test")
113112
def test_format_when_tainted_template_range_no_brackets_and_param_not_str_then_tainted(self):
114113
# type: () -> None
115114
self._assert_format_result(
@@ -119,7 +118,6 @@ def test_format_when_tainted_template_range_no_brackets_and_param_not_str_then_t
119118
escaped_expected_result=":+-<input1>template<input1>-+: 3.14",
120119
)
121120

122-
@pytest.mark.skip(reason="Migrating format_aspect to C++ breaks this test")
123121
def test_format_when_tainted_template_range_with_brackets_and_param_not_str_then_tainted(self):
124122
# type: () -> None
125123
self._assert_format_result(
@@ -159,9 +157,8 @@ def test_format_with_args_and_kwargs(self): # type: () -> None
159157
assert as_formatted_evidence(res) == "-1234 6 1"
160158

161159
string_input = create_taint_range_with_format(":+--12-+:34 {} {test_var}")
162-
mod.do_args_kwargs_1(string_input, 6, test_var=1) # pylint: disable=no-member
163-
# TODO format with params doesn't work correctly
164-
# assert as_formatted_evidence(res) == ":+--12-+:34 6 1"
160+
res = mod.do_args_kwargs_1(string_input, 6, test_var=1) # pylint: disable=no-member
161+
assert as_formatted_evidence(res) == ":+--12-+:34 6 1"
165162

166163
def test_format_with_one_argument_args_and_kwargs(self): # type: () -> None
167164
string_input = "-1234 {} {} {test_var}"
@@ -173,9 +170,8 @@ def test_format_with_one_argument_args_and_kwargs(self): # type: () -> None
173170
assert as_formatted_evidence(res) == "-1234 1 6 1"
174171

175172
string_input = create_taint_range_with_format(":+--12-+:34 {} {} {test_var}")
176-
mod.do_args_kwargs_2(string_input, 6, test_var=1) # pylint: disable=no-member
177-
# TODO format with params doesn't work correctly
178-
# as_formatted_evidence(res) == ":+--12-+:34 1 6 1"
173+
res = mod.do_args_kwargs_2(string_input, 6, test_var=1) # pylint: disable=no-member
174+
assert as_formatted_evidence(res) == ":+--12-+:34 1 6 1"
179175

180176
def test_format_with_two_argument_args_and_kwargs(self): # type: () -> None
181177
string_input = "-1234 {} {} {} {test_var}"
@@ -187,9 +183,8 @@ def test_format_with_two_argument_args_and_kwargs(self): # type: () -> None
187183
assert as_formatted_evidence(res) == "-1234 1 2 6 1"
188184

189185
string_input = create_taint_range_with_format(":+--12-+:34 {} {} {} {test_var}")
190-
mod.do_args_kwargs_3(string_input, 6, test_var=1) # pylint: disable=no-member
191-
# TODO format with params doesn't work correctly
192-
# as_formatted_evidence(res) == ":+--12-+:34 1 2 6 1"
186+
res = mod.do_args_kwargs_3(string_input, 6, test_var=1) # pylint: disable=no-member
187+
assert as_formatted_evidence(res) == ":+--12-+:34 1 2 6 1"
193188

194189
def test_format_with_two_argument_two_keywordargument_args_kwargs(self): # type: () -> None
195190
string_input = "-1234 {} {} {} {test_kwarg} {test_var}"
@@ -201,9 +196,8 @@ def test_format_with_two_argument_two_keywordargument_args_kwargs(self): # type
201196
assert as_formatted_evidence(res) == "-1234 1 2 6 3 1"
202197

203198
string_input = create_taint_range_with_format(":+--12-+:34 {} {} {} {test_kwarg} {test_var}")
204-
mod.do_args_kwargs_4(string_input, 6, test_var=1) # pylint: disable=no-member
205-
# TODO format with params doesn't work correctly
206-
# as_formatted_evidence(res) == ":+--12-+:34 1 2 6 3 1"
199+
res = mod.do_args_kwargs_4(string_input, 6, test_var=1) # pylint: disable=no-member
200+
assert as_formatted_evidence(res) == ":+--12-+:34 1 2 6 3 1"
207201

208202
def test_format_when_tainted_template_range_special_then_tainted_result(self): # type: () -> None
209203
self._assert_format_result(

tests/appsec/iast/aspects/test_format_map_aspect_fixtures.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,19 @@ def _assert_format_map_result(
3333

3434
assert as_formatted_evidence(result, tag_mapping_function=None) == escaped_expected_result
3535

36-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
3736
def test_format_map_when_template_is_none_then_raises_attribute_error(self): # type: () -> None
3837
with pytest.raises(AttributeError):
3938
mod.do_format_map(None, {})
4039

41-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
4240
def test_format_map_when_parameter_is_none_then_raises_type_error(self): # type: () -> None
4341
with pytest.raises(TypeError):
4442
assert mod.do_format_map("{key}", None) == "None"
4543

46-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
4744
def test_format_map_when_no_tainted_strings_then_no_tainted_result(self): # type: () -> None
4845
result = mod.do_format_map("template {key}", {"key": "parameter"})
4946
assert result, "template parameter"
5047
assert not get_ranges(result)
5148

52-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
5349
def test_format_map_when_tainted_parameter_then_tainted_result(self): # type: () -> None
5450
self._assert_format_map_result(
5551
taint_escaped_template="template {key}",
@@ -58,7 +54,6 @@ def test_format_map_when_tainted_parameter_then_tainted_result(self): # type: (
5854
escaped_expected_result="template :+-<input1>parameter<input1>-+:",
5955
)
6056

61-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
6257
def test_format_map_when_tainted_template_range_no_brackets_then_tainted_result(self): # type: () -> None
6358
self._assert_format_map_result(
6459
taint_escaped_template=":+-<input1>template<input1>-+: {key}",
@@ -67,7 +62,6 @@ def test_format_map_when_tainted_template_range_no_brackets_then_tainted_result(
6762
escaped_expected_result=":+-<input1>template<input1>-+: parameter",
6863
)
6964

70-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
7165
def test_format_map_when_tainted_template_range_with_brackets_then_tainted_result(self): # type: () -> None
7266
self._assert_format_map_result(
7367
taint_escaped_template="template :+-<input1>{key}<input1>-+:",
@@ -76,7 +70,6 @@ def test_format_map_when_tainted_template_range_with_brackets_then_tainted_resul
7670
escaped_expected_result="template :+-<input1>parameter<input1>-+:",
7771
)
7872

79-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
8073
def test_format_map_when_tainted_template_range_no_brackets_and_tainted_param_then_tainted(
8174
self,
8275
): # type: () -> None
@@ -87,7 +80,6 @@ def test_format_map_when_tainted_template_range_no_brackets_and_tainted_param_th
8780
escaped_expected_result=":+-<input1>template<input1>-+: " ":+-<input2>parameter<input2>-+:",
8881
)
8982

90-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
9183
def test_format_map_when_tainted_template_range_with_brackets_and_tainted_param_then_tainted(
9284
self,
9385
): # type: () -> None
@@ -98,7 +90,6 @@ def test_format_map_when_tainted_template_range_with_brackets_and_tainted_param_
9890
escaped_expected_result=":+-<input1>template <input1>-+:" ":+-<input2>parameter<input2>-+:",
9991
)
10092

101-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
10293
def test_format_map_when_ranges_overlap_then_give_preference_to_ranges_from_parameter(self): # type: () -> None
10394
self._assert_format_map_result(
10495
taint_escaped_template=":+-<input1>template {key} range overlapping<input1>-+:",
@@ -109,7 +100,6 @@ def test_format_map_when_ranges_overlap_then_give_preference_to_ranges_from_para
109100
":+-<input1> range overlapping<input1>-+:",
110101
)
111102

112-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
113103
def test_format_map_when_tainted_str_emoji_strings_then_tainted_result(self): # type: () -> None
114104
self._assert_format_map_result(
115105
taint_escaped_template=":+-<input1>template⚠️<input1>-+: {key}",
@@ -118,7 +108,6 @@ def test_format_map_when_tainted_str_emoji_strings_then_tainted_result(self): #
118108
escaped_expected_result=":+-<input1>template⚠️<input1>-+: " ":+-<input2>parameter⚠️<input2>-+:",
119109
)
120110

121-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
122111
def test_format_map_when_tainted_template_range_no_brackets_and_param_not_str_then_tainted(
123112
self,
124113
): # type: () -> None
@@ -129,7 +118,6 @@ def test_format_map_when_tainted_template_range_no_brackets_and_param_not_str_th
129118
escaped_expected_result=":+-<input1>template<input1>-+: 3.14",
130119
)
131120

132-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
133121
def test_format_map_when_tainted_template_range_with_brackets_and_param_not_str_then_tainted(
134122
self,
135123
): # type: () -> None
@@ -140,7 +128,6 @@ def test_format_map_when_tainted_template_range_with_brackets_and_param_not_str_
140128
escaped_expected_result=":+-<input1>template 3.14<input1>-+:",
141129
)
142130

143-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
144131
def test_format_map_when_texts_tainted_and_contain_escape_sequences_then_result_uncorrupted(
145132
self,
146133
): # type: () -> None
@@ -153,7 +140,6 @@ def test_format_map_when_texts_tainted_and_contain_escape_sequences_then_result_
153140
":+-<0>my_code<0>-+:",
154141
)
155142

156-
@pytest.mark.skipif(not hasattr("", "format_map"), reason="Method format_map not supported")
157143
def test_format_map_when_parameter_value_already_present_in_template_then_range_is_correct(
158144
self,
159145
): # type: () -> None

0 commit comments

Comments
 (0)