Skip to content

Commit

Permalink
feat(iast): report telemetry log error [backport 2.14] (#10784)
Browse files Browse the repository at this point in the history
Backport e237d91 from #10740 to 2.14.

Always report a telemetry log error when an IAST propagation error
raises, regardless of whether the `_DD_IAST_DEBUG` environment variable
is enabled or not.

- Fix some side effects of aspect functions in join_aspect, ljust_aspect
- Fix `format_aspect` "if not..." side-effect
- Fix `format_map_aspect` not BuiltinFunctionType
- Fix `_aspect_split` error task APPSEC-54782
- Early fail in `decode/encode_aspect`


Jira Task: APPSEC-54993

## Checklist
- [x] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [x] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- 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: Alberto Vara <[email protected]>
  • Loading branch information
github-actions[bot] and avara1986 committed Sep 25, 2024
1 parent d4a7535 commit 3acd4e7
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 82 deletions.
22 changes: 13 additions & 9 deletions ddtrace/appsec/_iast/_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ddtrace.appsec._constants import IAST
from ddtrace.appsec._constants import IAST_SPAN_TAGS
from ddtrace.appsec._deduplications import deduplication
from ddtrace.appsec._iast._utils import _is_iast_debug_enabled
from ddtrace.internal import telemetry
from ddtrace.internal.logger import get_logger
from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL
Expand Down Expand Up @@ -61,22 +62,25 @@ def wrapper(f):
def _set_iast_error_metric(msg: Text) -> None:
# Due to format_exc and format_exception returns the error and the last frame
try:
exception_type, exception_instance, _traceback_list = sys.exc_info()
res = []
# first 10 frames are this function, the exception in aspects and the error line
res.extend(traceback.format_stack(limit=10))
stack_trace = ""
if _is_iast_debug_enabled():
exception_type, exception_instance, _traceback_list = sys.exc_info()
res = []
# first 10 frames are this function, the exception in aspects and the error line
res.extend(traceback.format_stack(limit=10))

# get the frame with the error and the error message
result = traceback.format_exception(exception_type, exception_instance, _traceback_list)
res.extend(result[1:])
# get the frame with the error and the error message
result = traceback.format_exception(exception_type, exception_instance, _traceback_list)
res.extend(result[1:])

stack_trace = "".join(res)

stack_trace = "".join(res)
tags = {
"lib_language": "python",
}
telemetry.telemetry_writer.add_log(TELEMETRY_LOG_LEVEL.ERROR, msg, stack_trace=stack_trace, tags=tags)
except Exception:
log.warning("Error reporting ASM WAF logs metrics", exc_info=True)
log.warning("Error reporting ASM logs metrics", exc_info=True)


@metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY)
Expand Down
3 changes: 3 additions & 0 deletions ddtrace/appsec/_iast/_taint_tracking/Aspects/AspectIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ api_index_aspect(PyObject* self, PyObject* const* args, const Py_ssize_t nargs)
PyObject* candidate_text = args[0];
PyObject* idx = args[1];
const auto result_o = PyObject_GetItem(candidate_text, idx);
if (result_o == nullptr) {
return nullptr;
}
TRY_CATCH_ASPECT("index_aspect", return result_o, , {
if (const auto error = has_pyerr_as_string(); !error.empty()) {
iast_taint_log_error(error);
Expand Down
23 changes: 12 additions & 11 deletions ddtrace/appsec/_iast/_taint_tracking/Aspects/AspectJoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,19 @@ api_join_aspect(PyObject* self, PyObject* const* args, const Py_ssize_t nargs)
}
return nullptr;
}

const auto ctx_map = Initializer::get_tainting_map();
if (not ctx_map or ctx_map->empty() or get_pyobject_size(result) == 0) {
// Empty result cannot have taint ranges
TRY_CATCH_ASPECT("join_aspect", return result, , {
const auto ctx_map = Initializer::get_tainting_map();
if (not ctx_map or ctx_map->empty() or get_pyobject_size(result) == 0) {
// Empty result cannot have taint ranges
if (decref_arg0) {
Py_DecRef(arg0);
}
return result;
}
auto res = aspect_join(sep, result, arg0, ctx_map);
if (decref_arg0) {
Py_DecRef(arg0);
}
return result;
}
auto res = aspect_join(sep, result, arg0, ctx_map);
if (decref_arg0) {
Py_DecRef(arg0);
}
return res;
return res;
});
}
2 changes: 1 addition & 1 deletion ddtrace/appsec/_iast/_taint_tracking/Aspects/Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Example calling:
CLEANUP; \
RETURNRESULT; \
} catch (const std::exception& e) { \
const std::string error_message = "IAST propagation error in " NAME ". " + std::string(e.what()); \
const std::string error_message = NAME ". " + std::string(e.what()); \
iast_taint_log_error(error_message); \
CLEANUP; \
RETURNRESULT; \
Expand Down
5 changes: 2 additions & 3 deletions ddtrace/appsec/_iast/_taint_tracking/Utils/GenericUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ asbool(const char* value)
void
iast_taint_log_error(const std::string& msg)
{
safe_import("ddtrace.appsec._iast._metrics", "_set_iast_error_metric")("[IAST] Propagation error. " + msg);
try {
if (!is_iast_debug_enabled()) {
return;
Expand Down Expand Up @@ -64,9 +65,7 @@ iast_taint_log_error(const std::string& msg)
}

const auto log = get_python_logger();
log.attr("debug")(msg + ": " + frame_info);

safe_import("ddtrace.appsec._iast._metrics", "_set_iast_error_metric")("IAST propagation error. " + msg);
log.attr("debug")("[IAST] Propagation error. " + msg + ": " + frame_info);

// Restore the original exception state if needed
if (had_exception) {
Expand Down
11 changes: 3 additions & 8 deletions ddtrace/appsec/_iast/_taint_tracking/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import os
from typing import Any
from typing import Tuple

from ddtrace.internal._unpatched import _threading as threading
from ddtrace.internal.logger import get_logger
from ddtrace.internal.utils.formats import asbool

from ..._constants import IAST
from .._metrics import _set_iast_error_metric
from .._metrics import _set_metric_iast_executed_source
from .._utils import _is_iast_debug_enabled
from .._utils import _is_python_version_supported


Expand Down Expand Up @@ -112,18 +111,14 @@
]


def _is_iast_debug_enabled():
return asbool(os.environ.get(IAST.ENV_DEBUG, "false"))


def iast_taint_log_error(msg):
if _is_iast_debug_enabled():
import inspect

stack = inspect.stack()
frame_info = "\n".join("%s %s" % (frame_info.filename, frame_info.lineno) for frame_info in stack[:7])
log.debug("%s:\n%s", msg, frame_info)
_set_iast_error_metric("IAST propagation error. %s" % msg)
log.debug("[IAST] Propagation error. %s:\n%s", msg, frame_info)
_set_iast_error_metric("[IAST] Propagation error. %s" % msg)


def is_pyobject_tainted(pyobject: Any) -> bool:
Expand Down
Loading

0 comments on commit 3acd4e7

Please sign in to comment.