Skip to content
2 changes: 2 additions & 0 deletions ddtrace/appsec/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class IAST(object):
"""Specific constants for IAST"""

ENV = "DD_IAST_ENABLED"
ENV_DEBUG = "_DD_IAST_DEBUG"
TELEMETRY_REPORT_LVL = "DD_IAST_TELEMETRY_VERBOSITY"
JSON = "_dd.iast.json"
ENABLED = "_dd.iast.enabled"
CONTEXT_KEY = "_iast_data"
Expand Down
85 changes: 85 additions & 0 deletions ddtrace/appsec/iast/_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import os

from ddtrace.appsec._constants import IAST
from ddtrace.internal.logger import get_logger
from ddtrace.internal.telemetry import telemetry_metrics_writer
from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_IAST


log = get_logger(__name__)

TELEMETRY_OFF_NAME = "OFF"
TELEMETRY_DEBUG_NAME = "DEBUG"
TELEMETRY_MANDATORY_NAME = "MANDATORY"
TELEMETRY_INFORMATION_NAME = "INFORMATION"

TELEMETRY_DEBUG_VERBOSITY = 10
TELEMETRY_INFORMATION_VERBOSITY = 20
TELEMETRY_MANDATORY_VERBOSITY = 30
TELEMETRY_OFF_VERBOSITY = 40

METRICS_REPORT_LVLS = (
(TELEMETRY_DEBUG_VERBOSITY, TELEMETRY_DEBUG_NAME),
(TELEMETRY_INFORMATION_VERBOSITY, TELEMETRY_INFORMATION_NAME),
(TELEMETRY_MANDATORY_VERBOSITY, TELEMETRY_MANDATORY_NAME),
(TELEMETRY_OFF_VERBOSITY, TELEMETRY_OFF_NAME),
)


def get_iast_metrics_report_lvl(*args, **kwargs):
report_lvl_name = os.environ.get(IAST.TELEMETRY_REPORT_LVL, TELEMETRY_INFORMATION_NAME).upper()
report_lvl = 3
for lvl, lvl_name in METRICS_REPORT_LVLS:
if report_lvl_name == lvl_name:
return lvl
return report_lvl


def metric_verbosity(lvl):
def wrapper(f):
if lvl >= get_iast_metrics_report_lvl():
try:
return f
except Exception:
log.warning("Error reporting IAST metrics", exc_info=True)
return lambda: None # noqa: E731

return wrapper


@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
def _set_metric_iast_instrumented_source(source_type):
telemetry_metrics_writer.add_count_metric(
TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.source", 1, (("source_type", source_type),)
)


@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
def _set_metric_iast_instrumented_propagation():
telemetry_metrics_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.propagation", 1)


@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
def _set_metric_iast_instrumented_sink(vulnerability_type):
telemetry_metrics_writer.add_count_metric(
TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.sink", 1, (("vulnerability_type", vulnerability_type),)
)


@metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY)
def _set_metric_iast_executed_source(source_type):
telemetry_metrics_writer.add_count_metric(
TELEMETRY_NAMESPACE_TAG_IAST, "executed.source", 1, (("source_type", source_type),)
)


@metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY)
def _set_metric_iast_executed_sink(vulnerability_type):
telemetry_metrics_writer.add_count_metric(
TELEMETRY_NAMESPACE_TAG_IAST, "executed.sink", 1, (("vulnerability_type", vulnerability_type),)
)


@metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY)
def _set_metric_iast_request_tainted():
telemetry_metrics_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_IAST, "request.tainted", 1)
2 changes: 2 additions & 0 deletions ddtrace/appsec/iast/_taint_tracking/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import TYPE_CHECKING

from ddtrace.appsec.iast import oce
from ddtrace.appsec.iast._metrics import _set_metric_iast_executed_source
from ddtrace.appsec.iast._taint_dict import get_taint_dict
from ddtrace.appsec.iast._taint_tracking._native import new_pyobject_id
from ddtrace.appsec.iast._taint_tracking._native import setup # noqa: F401
Expand Down Expand Up @@ -52,6 +53,7 @@ def taint_pyobject(pyobject, input_info): # type: (Any, Input_info) -> Any
pyobject = new_pyobject_id(pyobject, len_pyobject)
taint_dict = get_taint_dict()
taint_dict[id(pyobject)] = ((input_info, 0, len_pyobject),)
_set_metric_iast_executed_source(input_info.origin)
return pyobject


Expand Down
2 changes: 2 additions & 0 deletions ddtrace/appsec/iast/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
RC4_DEF = "rc4"
IDEA_DEF = "idea"

DD_IAST_TELEMETRY_VERBOSITY = "DD_IAST_TELEMETRY_VERBOSITY"

DEFAULT_WEAK_HASH_ALGORITHMS = {MD5_DEF, SHA1_DEF}

DEFAULT_WEAK_CIPHER_ALGORITHMS = {DES_DEF, BLOWFISH_DEF, RC2_DEF, RC4_DEF, IDEA_DEF}
4 changes: 4 additions & 0 deletions ddtrace/appsec/iast/taint_sinks/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ddtrace import tracer
from ddtrace.appsec._constants import IAST
from ddtrace.appsec.iast import oce
from ddtrace.appsec.iast._metrics import _set_metric_iast_executed_sink
from ddtrace.appsec.iast._overhead_control_engine import Operation
from ddtrace.appsec.iast.reporter import Evidence
from ddtrace.appsec.iast.reporter import IastSpanReporter
Expand Down Expand Up @@ -89,6 +90,9 @@ def report(cls, evidence_value="", sources=None):
evidence = ""

if cls.is_not_reported(file_name, line_number):

_set_metric_iast_executed_sink(cls.vulnerability_type)

report = _context.get_item(IAST.CONTEXT_KEY, span=span)
if report:
report.vulnerabilities.add(
Expand Down
1 change: 1 addition & 0 deletions ddtrace/internal/telemetry/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

TELEMETRY_NAMESPACE_TAG_TRACER = "tracers"
TELEMETRY_NAMESPACE_TAG_APPSEC = "appsec"
TELEMETRY_NAMESPACE_TAG_IAST = "iast"

TELEMETRY_TYPE_GENERATE_METRICS = "generate-metrics"
TELEMETRY_TYPE_DISTRIBUTION = "distributions"
Expand Down
29 changes: 29 additions & 0 deletions tests/appsec/iast/test_telemety.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest

from ddtrace.appsec.iast._metrics import TELEMETRY_DEBUG_VERBOSITY
from ddtrace.appsec.iast._metrics import TELEMETRY_INFORMATION_VERBOSITY
from ddtrace.appsec.iast._metrics import TELEMETRY_MANDATORY_VERBOSITY
from ddtrace.appsec.iast._metrics import metric_verbosity
from tests.utils import override_env


@pytest.mark.parametrize(
"lvl, env_lvl, expected_result",
[
(TELEMETRY_DEBUG_VERBOSITY, "OFF", None),
(TELEMETRY_MANDATORY_VERBOSITY, "OFF", None),
(TELEMETRY_INFORMATION_VERBOSITY, "OFF", None),
(TELEMETRY_DEBUG_VERBOSITY, "DEBUG", 1),
(TELEMETRY_MANDATORY_VERBOSITY, "DEBUG", 1),
(TELEMETRY_INFORMATION_VERBOSITY, "DEBUG", 1),
(TELEMETRY_DEBUG_VERBOSITY, "INFORMATION", None),
(TELEMETRY_INFORMATION_VERBOSITY, "INFORMATION", 1),
(TELEMETRY_MANDATORY_VERBOSITY, "INFORMATION", 1),
(TELEMETRY_DEBUG_VERBOSITY, "MANDATORY", None),
(TELEMETRY_INFORMATION_VERBOSITY, "MANDATORY", None),
(TELEMETRY_MANDATORY_VERBOSITY, "MANDATORY", 1),
],
)
def test_metric_verbosity(lvl, env_lvl, expected_result):
with override_env(dict(DD_IAST_TELEMETRY_VERBOSITY=env_lvl)):
assert metric_verbosity(lvl)(lambda: 1)() == expected_result