Skip to content

Commit 9aa89e6

Browse files
avara1986juanjux
andauthored
feat(iast): iast telemetry metrics base (#6227)
Implement executed.source metric ## 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) --------- Co-authored-by: Juanjo Alvarez Martinez <[email protected]>
1 parent a346cdd commit 9aa89e6

File tree

7 files changed

+125
-0
lines changed

7 files changed

+125
-0
lines changed

ddtrace/appsec/_constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class IAST(object):
6363
"""Specific constants for IAST"""
6464

6565
ENV = "DD_IAST_ENABLED"
66+
ENV_DEBUG = "_DD_IAST_DEBUG"
67+
TELEMETRY_REPORT_LVL = "DD_IAST_TELEMETRY_VERBOSITY"
6668
JSON = "_dd.iast.json"
6769
ENABLED = "_dd.iast.enabled"
6870
CONTEXT_KEY = "_iast_data"

ddtrace/appsec/iast/_metrics.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import os
2+
3+
from ddtrace.appsec._constants import IAST
4+
from ddtrace.internal.logger import get_logger
5+
from ddtrace.internal.telemetry import telemetry_metrics_writer
6+
from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_IAST
7+
8+
9+
log = get_logger(__name__)
10+
11+
TELEMETRY_OFF_NAME = "OFF"
12+
TELEMETRY_DEBUG_NAME = "DEBUG"
13+
TELEMETRY_MANDATORY_NAME = "MANDATORY"
14+
TELEMETRY_INFORMATION_NAME = "INFORMATION"
15+
16+
TELEMETRY_DEBUG_VERBOSITY = 10
17+
TELEMETRY_INFORMATION_VERBOSITY = 20
18+
TELEMETRY_MANDATORY_VERBOSITY = 30
19+
TELEMETRY_OFF_VERBOSITY = 40
20+
21+
METRICS_REPORT_LVLS = (
22+
(TELEMETRY_DEBUG_VERBOSITY, TELEMETRY_DEBUG_NAME),
23+
(TELEMETRY_INFORMATION_VERBOSITY, TELEMETRY_INFORMATION_NAME),
24+
(TELEMETRY_MANDATORY_VERBOSITY, TELEMETRY_MANDATORY_NAME),
25+
(TELEMETRY_OFF_VERBOSITY, TELEMETRY_OFF_NAME),
26+
)
27+
28+
29+
def get_iast_metrics_report_lvl(*args, **kwargs):
30+
report_lvl_name = os.environ.get(IAST.TELEMETRY_REPORT_LVL, TELEMETRY_INFORMATION_NAME).upper()
31+
report_lvl = 3
32+
for lvl, lvl_name in METRICS_REPORT_LVLS:
33+
if report_lvl_name == lvl_name:
34+
return lvl
35+
return report_lvl
36+
37+
38+
def metric_verbosity(lvl):
39+
def wrapper(f):
40+
if lvl >= get_iast_metrics_report_lvl():
41+
try:
42+
return f
43+
except Exception:
44+
log.warning("Error reporting IAST metrics", exc_info=True)
45+
return lambda: None # noqa: E731
46+
47+
return wrapper
48+
49+
50+
@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
51+
def _set_metric_iast_instrumented_source(source_type):
52+
telemetry_metrics_writer.add_count_metric(
53+
TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.source", 1, (("source_type", source_type),)
54+
)
55+
56+
57+
@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
58+
def _set_metric_iast_instrumented_propagation():
59+
telemetry_metrics_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.propagation", 1)
60+
61+
62+
@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
63+
def _set_metric_iast_instrumented_sink(vulnerability_type):
64+
telemetry_metrics_writer.add_count_metric(
65+
TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.sink", 1, (("vulnerability_type", vulnerability_type),)
66+
)
67+
68+
69+
@metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY)
70+
def _set_metric_iast_executed_source(source_type):
71+
telemetry_metrics_writer.add_count_metric(
72+
TELEMETRY_NAMESPACE_TAG_IAST, "executed.source", 1, (("source_type", source_type),)
73+
)
74+
75+
76+
@metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY)
77+
def _set_metric_iast_executed_sink(vulnerability_type):
78+
telemetry_metrics_writer.add_count_metric(
79+
TELEMETRY_NAMESPACE_TAG_IAST, "executed.sink", 1, (("vulnerability_type", vulnerability_type),)
80+
)
81+
82+
83+
@metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY)
84+
def _set_metric_iast_request_tainted():
85+
telemetry_metrics_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_IAST, "request.tainted", 1)

ddtrace/appsec/iast/_taint_tracking/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import TYPE_CHECKING
44

55
from ddtrace.appsec.iast import oce
6+
from ddtrace.appsec.iast._metrics import _set_metric_iast_executed_source
67
from ddtrace.appsec.iast._taint_dict import get_taint_dict
78
from ddtrace.appsec.iast._taint_tracking._native import new_pyobject_id
89
from ddtrace.appsec.iast._taint_tracking._native import setup # noqa: F401
@@ -52,6 +53,7 @@ def taint_pyobject(pyobject, input_info): # type: (Any, Input_info) -> Any
5253
pyobject = new_pyobject_id(pyobject, len_pyobject)
5354
taint_dict = get_taint_dict()
5455
taint_dict[id(pyobject)] = ((input_info, 0, len_pyobject),)
56+
_set_metric_iast_executed_source(input_info.origin)
5557
return pyobject
5658

5759

ddtrace/appsec/iast/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
RC4_DEF = "rc4"
2222
IDEA_DEF = "idea"
2323

24+
DD_IAST_TELEMETRY_VERBOSITY = "DD_IAST_TELEMETRY_VERBOSITY"
25+
2426
DEFAULT_WEAK_HASH_ALGORITHMS = {MD5_DEF, SHA1_DEF}
2527

2628
DEFAULT_WEAK_CIPHER_ALGORITHMS = {DES_DEF, BLOWFISH_DEF, RC2_DEF, RC4_DEF, IDEA_DEF}

ddtrace/appsec/iast/taint_sinks/_base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from ddtrace import tracer
55
from ddtrace.appsec._constants import IAST
66
from ddtrace.appsec.iast import oce
7+
from ddtrace.appsec.iast._metrics import _set_metric_iast_executed_sink
78
from ddtrace.appsec.iast._overhead_control_engine import Operation
89
from ddtrace.appsec.iast.reporter import Evidence
910
from ddtrace.appsec.iast.reporter import IastSpanReporter
@@ -89,6 +90,9 @@ def report(cls, evidence_value="", sources=None):
8990
evidence = ""
9091

9192
if cls.is_not_reported(file_name, line_number):
93+
94+
_set_metric_iast_executed_sink(cls.vulnerability_type)
95+
9296
report = _context.get_item(IAST.CONTEXT_KEY, span=span)
9397
if report:
9498
report.vulnerabilities.add(

ddtrace/internal/telemetry/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
TELEMETRY_NAMESPACE_TAG_TRACER = "tracers"
77
TELEMETRY_NAMESPACE_TAG_APPSEC = "appsec"
8+
TELEMETRY_NAMESPACE_TAG_IAST = "iast"
89

910
TELEMETRY_TYPE_GENERATE_METRICS = "generate-metrics"
1011
TELEMETRY_TYPE_DISTRIBUTION = "distributions"

tests/appsec/iast/test_telemety.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import pytest
2+
3+
from ddtrace.appsec.iast._metrics import TELEMETRY_DEBUG_VERBOSITY
4+
from ddtrace.appsec.iast._metrics import TELEMETRY_INFORMATION_VERBOSITY
5+
from ddtrace.appsec.iast._metrics import TELEMETRY_MANDATORY_VERBOSITY
6+
from ddtrace.appsec.iast._metrics import metric_verbosity
7+
from tests.utils import override_env
8+
9+
10+
@pytest.mark.parametrize(
11+
"lvl, env_lvl, expected_result",
12+
[
13+
(TELEMETRY_DEBUG_VERBOSITY, "OFF", None),
14+
(TELEMETRY_MANDATORY_VERBOSITY, "OFF", None),
15+
(TELEMETRY_INFORMATION_VERBOSITY, "OFF", None),
16+
(TELEMETRY_DEBUG_VERBOSITY, "DEBUG", 1),
17+
(TELEMETRY_MANDATORY_VERBOSITY, "DEBUG", 1),
18+
(TELEMETRY_INFORMATION_VERBOSITY, "DEBUG", 1),
19+
(TELEMETRY_DEBUG_VERBOSITY, "INFORMATION", None),
20+
(TELEMETRY_INFORMATION_VERBOSITY, "INFORMATION", 1),
21+
(TELEMETRY_MANDATORY_VERBOSITY, "INFORMATION", 1),
22+
(TELEMETRY_DEBUG_VERBOSITY, "MANDATORY", None),
23+
(TELEMETRY_INFORMATION_VERBOSITY, "MANDATORY", None),
24+
(TELEMETRY_MANDATORY_VERBOSITY, "MANDATORY", 1),
25+
],
26+
)
27+
def test_metric_verbosity(lvl, env_lvl, expected_result):
28+
with override_env(dict(DD_IAST_TELEMETRY_VERBOSITY=env_lvl)):
29+
assert metric_verbosity(lvl)(lambda: 1)() == expected_result

0 commit comments

Comments
 (0)