Skip to content

Commit ac0fdad

Browse files
authored
fix(iast): improve traceback message from telemetry logs [Backport 1.20] (#7522)
Backport #7510 to 1.20. ## 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 05b3442 commit ac0fdad

File tree

3 files changed

+46
-24
lines changed

3 files changed

+46
-24
lines changed

ddtrace/appsec/iast/_metrics.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
import sys
3+
import traceback
24

35
from ddtrace.appsec._constants import IAST
46
from ddtrace.appsec._deduplications import deduplication
@@ -50,9 +52,20 @@ def wrapper(f):
5052

5153
@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
5254
@deduplication
53-
def _set_iast_error_metric(msg, stack_trace):
54-
# type: (str, str) -> None
55+
def _set_iast_error_metric(msg):
56+
# type: (str) -> None
57+
# Due to format_exc and format_exception returns the error and the last frame
5558
try:
59+
exception_type, exception_instance, _traceback_list = sys.exc_info()
60+
res = []
61+
# first 3 frames are this function, the exception in aspects and the error line
62+
res.extend(traceback.format_stack(limit=10)[:-3])
63+
64+
# get the frame with the error and the error message
65+
result = traceback.format_exception(exception_type, exception_instance, _traceback_list)
66+
res.extend(result[1:])
67+
68+
stack_trace = "".join(res)
5669
tags = {
5770
"lib_language": "python",
5871
}

ddtrace/appsec/iast/_taint_tracking/aspects.py

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from builtins import bytes as builtin_bytes
33
from builtins import str as builtin_str
44
import codecs
5-
import traceback
65
from typing import TYPE_CHECKING
76

87
from ddtrace.internal.compat import iteritems
@@ -63,7 +62,7 @@ def str_aspect(*args, **kwargs):
6362
if new_ranges:
6463
taint_pyobject_with_ranges(result, tuple(new_ranges))
6564
except Exception as e:
66-
_set_iast_error_metric("IAST propagation error. str_aspect. {}".format(e), traceback.format_exc())
65+
_set_iast_error_metric("IAST propagation error. str_aspect. {}".format(e))
6766
return result
6867

6968

@@ -74,7 +73,7 @@ def bytes_aspect(*args, **kwargs):
7473
try:
7574
taint_pyobject_with_ranges(result, tuple(get_ranges(args[0])))
7675
except Exception as e:
77-
_set_iast_error_metric("IAST propagation error. bytes_aspect. {}".format(e), traceback.format_exc())
76+
_set_iast_error_metric("IAST propagation error. bytes_aspect. {}".format(e))
7877
return result
7978

8079

@@ -85,7 +84,7 @@ def bytearray_aspect(*args, **kwargs):
8584
try:
8685
taint_pyobject_with_ranges(result, tuple(get_ranges(args[0])))
8786
except Exception as e:
88-
_set_iast_error_metric("IAST propagation error. bytearray_aspect. {}".format(e), traceback.format_exc())
87+
_set_iast_error_metric("IAST propagation error. bytearray_aspect. {}".format(e))
8988
return result
9089

9190

@@ -96,7 +95,7 @@ def join_aspect(joiner, *args, **kwargs):
9695
try:
9796
return _join_aspect(joiner, *args, **kwargs)
9897
except Exception as e:
99-
_set_iast_error_metric("IAST propagation error. join_aspect. {}".format(e), traceback.format_exc())
98+
_set_iast_error_metric("IAST propagation error. join_aspect. {}".format(e))
10099
return joiner.join(*args, **kwargs)
101100

102101

@@ -107,7 +106,7 @@ def bytearray_extend_aspect(op1, op2):
107106
try:
108107
return _extend_aspect(op1, op2)
109108
except Exception as e:
110-
_set_iast_error_metric("IAST propagation error. extend_aspect. {}".format(e), traceback.format_exc())
109+
_set_iast_error_metric("IAST propagation error. extend_aspect. {}".format(e))
111110
return op1.extend(op2)
112111

113112

@@ -144,7 +143,7 @@ def modulo_aspect(candidate_text, candidate_tuple):
144143
ranges_orig=ranges_orig,
145144
)
146145
except Exception as e:
147-
_set_iast_error_metric("IAST propagation error. modulo_aspect. {}".format(e), traceback.format_exc())
146+
_set_iast_error_metric("IAST propagation error. modulo_aspect. {}".format(e))
148147
return candidate_text % candidate_tuple
149148

150149

@@ -171,7 +170,7 @@ def ljust_aspect(candidate_text, *args, **kwargs):
171170
taint_pyobject_with_ranges(res, ranges_new)
172171
return res
173172
except Exception as e:
174-
_set_iast_error_metric("IAST propagation error. ljust_aspect. {}".format(e), traceback.format_exc())
173+
_set_iast_error_metric("IAST propagation error. ljust_aspect. {}".format(e))
175174
return candidate_text.ljust(*args, **kwargs)
176175

177176

@@ -204,7 +203,7 @@ def zfill_aspect(candidate_text, *args, **kwargs):
204203
taint_pyobject_with_ranges(res, tuple(ranges_new))
205204
return res
206205
except Exception as e:
207-
_set_iast_error_metric("IAST propagation error. format_aspect. {}".format(e), traceback.format_exc())
206+
_set_iast_error_metric("IAST propagation error. format_aspect. {}".format(e))
208207
return candidate_text.zfill(*args, **kwargs)
209208

210209

@@ -244,7 +243,7 @@ def format_aspect(
244243
)
245244
return result
246245
except Exception as e:
247-
_set_iast_error_metric("IAST propagation error. format_aspect. {}".format(e), traceback.format_exc())
246+
_set_iast_error_metric("IAST propagation error. format_aspect. {}".format(e))
248247
return candidate_text.format(*args, **kwargs)
249248

250249

@@ -275,7 +274,7 @@ def format_map_aspect(candidate_text, *args, **kwargs): # type: (str, Any, Any)
275274
ranges_orig=ranges_orig,
276275
)
277276
except Exception as e:
278-
_set_iast_error_metric("IAST propagation error. format_map_aspect. {}".format(e), traceback.format_exc())
277+
_set_iast_error_metric("IAST propagation error. format_map_aspect. {}".format(e))
279278
return candidate_text.format_map(*args, **kwargs)
280279

281280

@@ -293,7 +292,7 @@ def repr_aspect(*args, **kwargs):
293292
if new_ranges:
294293
taint_pyobject_with_ranges(result, tuple(new_ranges))
295294
except Exception as e:
296-
_set_iast_error_metric("IAST propagation error. repr_aspect. {}".format(e), traceback.format_exc())
295+
_set_iast_error_metric("IAST propagation error. repr_aspect. {}".format(e))
297296
return result
298297

299298

@@ -335,7 +334,7 @@ def format_value_aspect(
335334
else:
336335
return str_aspect(new_text)
337336
except Exception as e:
338-
_set_iast_error_metric("IAST propagation error. format_value_aspect. {}".format(e), traceback.format_exc())
337+
_set_iast_error_metric("IAST propagation error. format_value_aspect. {}".format(e))
339338
return new_text
340339

341340

@@ -387,7 +386,7 @@ def decode_aspect(self, *args, **kwargs):
387386
inc_dec = codecs.getincrementaldecoder(codec)(**kwargs)
388387
return incremental_translation(self, inc_dec, inc_dec.decode, "")
389388
except Exception as e:
390-
_set_iast_error_metric("IAST propagation error. decode_aspect. {}".format(e), traceback.format_exc())
389+
_set_iast_error_metric("IAST propagation error. decode_aspect. {}".format(e))
391390
return self.decode(*args, **kwargs)
392391

393392

@@ -399,7 +398,7 @@ def encode_aspect(self, *args, **kwargs):
399398
inc_enc = codecs.getincrementalencoder(codec)(**kwargs)
400399
return incremental_translation(self, inc_enc, inc_enc.encode, b"")
401400
except Exception as e:
402-
_set_iast_error_metric("IAST propagation error. encode_aspect. {}".format(e), traceback.format_exc())
401+
_set_iast_error_metric("IAST propagation error. encode_aspect. {}".format(e))
403402
return self.encode(*args, **kwargs)
404403

405404

@@ -410,7 +409,7 @@ def upper_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any) -> T
410409
try:
411410
return common_replace("upper", candidate_text, *args, **kwargs)
412411
except Exception as e:
413-
_set_iast_error_metric("IAST propagation error. upper_aspect. {}".format(e), traceback.format_exc())
412+
_set_iast_error_metric("IAST propagation error. upper_aspect. {}".format(e))
414413
return candidate_text.upper(*args, **kwargs)
415414

416415

@@ -421,7 +420,7 @@ def lower_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any) -> T
421420
try:
422421
return common_replace("lower", candidate_text, *args, **kwargs)
423422
except Exception as e:
424-
_set_iast_error_metric("IAST propagation error. lower_aspect. {}".format(e), traceback.format_exc())
423+
_set_iast_error_metric("IAST propagation error. lower_aspect. {}".format(e))
425424
return candidate_text.lower(*args, **kwargs)
426425

427426

@@ -431,7 +430,7 @@ def swapcase_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any) -
431430
try:
432431
return common_replace("swapcase", candidate_text, *args, **kwargs)
433432
except Exception as e:
434-
_set_iast_error_metric("IAST propagation error. swapcase_aspect. {}".format(e), traceback.format_exc())
433+
_set_iast_error_metric("IAST propagation error. swapcase_aspect. {}".format(e))
435434
return candidate_text.swapcase(*args, **kwargs)
436435

437436

@@ -441,7 +440,7 @@ def title_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any) -> T
441440
try:
442441
return common_replace("title", candidate_text, *args, **kwargs)
443442
except Exception as e:
444-
_set_iast_error_metric("IAST propagation error. title_aspect. {}".format(e), traceback.format_exc())
443+
_set_iast_error_metric("IAST propagation error. title_aspect. {}".format(e))
445444
return candidate_text.title(*args, **kwargs)
446445

447446

@@ -452,7 +451,7 @@ def capitalize_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any)
452451
try:
453452
return common_replace("capitalize", candidate_text, *args, **kwargs)
454453
except Exception as e:
455-
_set_iast_error_metric("IAST propagation error. capitalize_aspect. {}".format(e), traceback.format_exc())
454+
_set_iast_error_metric("IAST propagation error. capitalize_aspect. {}".format(e))
456455
return candidate_text.capitalize(*args, **kwargs)
457456

458457

@@ -462,7 +461,7 @@ def casefold_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any) -
462461
try:
463462
return common_replace("casefold", candidate_text, *args, **kwargs)
464463
except Exception as e:
465-
_set_iast_error_metric("IAST propagation error. casefold_aspect. {}".format(e), traceback.format_exc())
464+
_set_iast_error_metric("IAST propagation error. casefold_aspect. {}".format(e))
466465
return candidate_text.casefold(*args, **kwargs) # type: ignore[union-attr]
467466

468467

@@ -472,7 +471,7 @@ def translate_aspect(candidate_text, *args, **kwargs): # type: (Any, Any, Any)
472471
try:
473472
return common_replace("translate", candidate_text, *args, **kwargs)
474473
except Exception as e:
475-
_set_iast_error_metric("IAST propagation error. translate_aspect. {}".format(e), traceback.format_exc())
474+
_set_iast_error_metric("IAST propagation error. translate_aspect. {}".format(e))
476475
return candidate_text.translate(*args, **kwargs)
477476

478477

tests/appsec/iast/test_telemetry.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from ddtrace.appsec.iast._metrics import TELEMETRY_DEBUG_VERBOSITY
66
from ddtrace.appsec.iast._metrics import TELEMETRY_INFORMATION_VERBOSITY
77
from ddtrace.appsec.iast._metrics import TELEMETRY_MANDATORY_VERBOSITY
8+
from ddtrace.appsec.iast._metrics import _set_iast_error_metric
89
from ddtrace.appsec.iast._metrics import metric_verbosity
910
from ddtrace.appsec.iast._patch_modules import patch_iast
1011
from ddtrace.appsec.iast._taint_tracking import OriginType
@@ -105,3 +106,12 @@ def test_metric_request_tainted(mock_telemetry_lifecycle_writer):
105106
generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_IAST]
106107
assert len(generate_metrics) == 2, "Expected 1 generate_metrics"
107108
assert [metric.name for metric in generate_metrics.values()] == ["executed.source", "request.tainted"]
109+
110+
111+
def test_log_metric(mock_telemetry_lifecycle_writer):
112+
_set_iast_error_metric("test_format_key_error_and_no_log_metric raises")
113+
114+
list_metrics_logs = list(mock_telemetry_lifecycle_writer._logs)
115+
assert len(list_metrics_logs) == 1
116+
assert list_metrics_logs[0]["message"] == "test_format_key_error_and_no_log_metric raises"
117+
assert str(list_metrics_logs[0]["stack_trace"]).startswith(' File "/')

0 commit comments

Comments
 (0)