|
1 | 1 | import os |
| 2 | +import time |
2 | 3 | from typing import TYPE_CHECKING |
3 | 4 | from typing import cast |
4 | 5 |
|
|
10 | 11 | from ddtrace.internal.utils.cache import LFUCache |
11 | 12 | from ddtrace.settings.asm import config as asm_config |
12 | 13 |
|
| 14 | +from ..._deduplications import deduplication |
13 | 15 | from .. import oce |
14 | 16 | from .._overhead_control_engine import Operation |
15 | 17 | from .._utils import _has_to_scrub |
|
44 | 46 | CWD = os.path.abspath(os.getcwd()) |
45 | 47 |
|
46 | 48 |
|
| 49 | +class taint_sink_deduplication(deduplication): |
| 50 | + def __call__(self, *args, **kwargs): |
| 51 | + # we skip 0, 1 and last position because its the cls, span and sources respectively |
| 52 | + result = None |
| 53 | + if self.is_deduplication_enabled() is False: |
| 54 | + result = self.func(*args, **kwargs) |
| 55 | + else: |
| 56 | + raw_log_hash = hash("".join([str(arg) for arg in args[2:-1]])) |
| 57 | + last_reported_timestamp = self.get_last_time_reported(raw_log_hash) |
| 58 | + if time.time() > last_reported_timestamp: |
| 59 | + result = self.func(*args, **kwargs) |
| 60 | + self.reported_logs[raw_log_hash] = time.time() + self._time_lapse |
| 61 | + return result |
| 62 | + |
| 63 | + |
47 | 64 | def _check_positions_contained(needle, container): |
48 | 65 | needle_start, needle_end = needle |
49 | 66 | container_start, container_end = container |
@@ -81,13 +98,51 @@ def wrapper(wrapped, instance, args, kwargs): |
81 | 98 |
|
82 | 99 | return wrapper |
83 | 100 |
|
| 101 | + @classmethod |
| 102 | + @taint_sink_deduplication |
| 103 | + def _prepare_report(cls, span, vulnerability_type, evidence, file_name, line_number, sources): |
| 104 | + report = core.get_item(IAST.CONTEXT_KEY, span=span) |
| 105 | + if report: |
| 106 | + report.vulnerabilities.add( |
| 107 | + Vulnerability( |
| 108 | + type=vulnerability_type, |
| 109 | + evidence=evidence, |
| 110 | + location=Location(path=file_name, line=line_number, spanId=span.span_id), |
| 111 | + ) |
| 112 | + ) |
| 113 | + |
| 114 | + else: |
| 115 | + report = IastSpanReporter( |
| 116 | + vulnerabilities={ |
| 117 | + Vulnerability( |
| 118 | + type=vulnerability_type, |
| 119 | + evidence=evidence, |
| 120 | + location=Location(path=file_name, line=line_number, spanId=span.span_id), |
| 121 | + ) |
| 122 | + } |
| 123 | + ) |
| 124 | + if sources: |
| 125 | + |
| 126 | + def cast_value(value): |
| 127 | + if isinstance(value, (bytes, bytearray)): |
| 128 | + value_decoded = value.decode("utf-8") |
| 129 | + else: |
| 130 | + value_decoded = value |
| 131 | + return value_decoded |
| 132 | + |
| 133 | + report.sources = [Source(origin=x.origin, name=x.name, value=cast_value(x.value)) for x in sources] |
| 134 | + |
| 135 | + redacted_report = cls._redacted_report_cache.get( |
| 136 | + hash(report), lambda x: cls._redact_report(cast(IastSpanReporter, report)) |
| 137 | + ) |
| 138 | + core.set_item(IAST.CONTEXT_KEY, redacted_report, span=span) |
| 139 | + |
| 140 | + return True |
| 141 | + |
84 | 142 | @classmethod |
85 | 143 | def report(cls, evidence_value="", sources=None): |
86 | 144 | # type: (Union[Text|List[Dict[str, Any]]], Optional[List[Source]]) -> None |
87 | | - """Build a IastSpanReporter instance to report it in the `AppSecIastSpanProcessor` as a string JSON |
88 | | -
|
89 | | - TODO: check deduplications if DD_IAST_DEDUPLICATION_ENABLED is true |
90 | | - """ |
| 145 | + """Build a IastSpanReporter instance to report it in the `AppSecIastSpanProcessor` as a string JSON""" |
91 | 146 |
|
92 | 147 | if cls.acquire_quota(): |
93 | 148 | if not tracer or not hasattr(tracer, "current_root_span"): |
@@ -131,41 +186,11 @@ def report(cls, evidence_value="", sources=None): |
131 | 186 | log.debug("Unexpected evidence_value type: %s", type(evidence_value)) |
132 | 187 | evidence = Evidence(value="") |
133 | 188 |
|
134 | | - report = core.get_item(IAST.CONTEXT_KEY, span=span) |
135 | | - if report: |
136 | | - report.vulnerabilities.add( |
137 | | - Vulnerability( |
138 | | - type=cls.vulnerability_type, |
139 | | - evidence=evidence, |
140 | | - location=Location(path=file_name, line=line_number, spanId=span.span_id), |
141 | | - ) |
142 | | - ) |
143 | | - |
144 | | - else: |
145 | | - report = IastSpanReporter( |
146 | | - vulnerabilities={ |
147 | | - Vulnerability( |
148 | | - type=cls.vulnerability_type, |
149 | | - evidence=evidence, |
150 | | - location=Location(path=file_name, line=line_number, spanId=span.span_id), |
151 | | - ) |
152 | | - } |
153 | | - ) |
154 | | - if sources: |
155 | | - |
156 | | - def cast_value(value): |
157 | | - if isinstance(value, (bytes, bytearray)): |
158 | | - value_decoded = value.decode("utf-8") |
159 | | - else: |
160 | | - value_decoded = value |
161 | | - return value_decoded |
162 | | - |
163 | | - report.sources = [Source(origin=x.origin, name=x.name, value=cast_value(x.value)) for x in sources] |
164 | | - |
165 | | - redacted_report = cls._redacted_report_cache.get( |
166 | | - hash(report), lambda x: cls._redact_report(cast(IastSpanReporter, report)) |
167 | | - ) |
168 | | - core.set_item(IAST.CONTEXT_KEY, redacted_report, span=span) |
| 189 | + result = cls._prepare_report(span, cls.vulnerability_type, evidence, file_name, line_number, sources) |
| 190 | + # If result is None that's mean deduplication raises and no vulnerability wasn't reported, with that, |
| 191 | + # we need to restore the quota |
| 192 | + if not result: |
| 193 | + cls.increment_quota() |
169 | 194 |
|
170 | 195 | @classmethod |
171 | 196 | def _extract_sensitive_tokens(cls, report): |
|
0 commit comments