Skip to content

APMSP-3552 Redact sensitive data in DI log-probe messages - #6155

Open
p-datadog wants to merge 6 commits into
masterfrom
di-message-serializer-redaction
Open

APMSP-3552 Redact sensitive data in DI log-probe messages#6155
p-datadog wants to merge 6 commits into
masterfrom
di-message-serializer-redaction

Conversation

@p-datadog

@p-datadog p-datadog commented Aug 5, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Redacts DI log probe messages the same way that the values are redacted in snapshots.

Motivation:

Currently it is possible to bypass value redaction by explicitly referencing values in the message template.

The behavior of most tracers is to redact in messages also. This PR makes Ruby aligned with the majority of tracers.

Change log entry

Yes. Dynamic Instrumentation now redacts sensitive identifiers and types in log probe messages, matching the redaction already applied to captured snapshots.

How to test the change?

Unit tests added.

serialize_value_for_message rendered EL expression results into log-probe
messages without consulting the Redactor, so a template referencing an
object or hash (e.g. {user}, {params}) interpolated every instance
variable and hash entry verbatim. Identifiers on the redaction list
(@password, @api_key, session-key, ...) and values of redacted types were
emitted in plaintext to the debugger intake, even though the snapshot path
(serialize_value) redacts them.

Gate the message path the same way as the snapshot path: redact by type at
entry, and replace hash-entry and instance-variable values whose name
matches a redacted identifier with a [redacted] placeholder.
@p-datadog p-datadog added the AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos label Aug 5, 2026
@dd-octo-sts dd-octo-sts Bot added the debugger Live Debugger (+Dynamic Instrumentation, +Symbol Database) label Aug 5, 2026
@dd-octo-sts

dd-octo-sts Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Typing analysis

Note: Ignored files are excluded from the next sections.

steep:ignore comments

This PR introduces 8 steep:ignore comments, and clears 8 steep:ignore comments.

steep:ignore comments (+8-8)Introduced:
lib/datadog/di/el/compiler.rb:60
lib/datadog/di/el/compiler.rb:72
lib/datadog/di/el/compiler.rb:82
lib/datadog/di/el/compiler.rb:85
lib/datadog/di/el/expression.rb:39
lib/datadog/di/serializer.rb:264
lib/datadog/di/serializer.rb:404
lib/datadog/di/serializer.rb:529
Cleared:
lib/datadog/di/el/compiler.rb:37
lib/datadog/di/el/compiler.rb:49
lib/datadog/di/el/compiler.rb:59
lib/datadog/di/el/compiler.rb:62
lib/datadog/di/el/expression.rb:34
lib/datadog/di/serializer.rb:257
lib/datadog/di/serializer.rb:389
lib/datadog/di/serializer.rb:504

Untyped methods

This PR introduces 5 partially typed methods, and clears 5 partially typed methods. It increases the percentage of typed methods from 69.48% to 69.49% (+0.01%).

Partially typed methods (+5-5)Introduced:
sig/datadog/di/serializer.rbs:25
└── def combine_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, Object target_self) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:26
└── def serialize_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, untyped instance_vars, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:27
└── def serialize_vars: (Hash[Symbol, untyped] vars, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:28
└── def serialize_value: (any value, ?name: (Symbol | String)?, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?, ?type: Class?) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:31
└── def self.register: (?condition: Proc?) {
        (Serializer serializer, any value, name: ::Symbol?, depth: ::Integer, ?attribute_count: ::Integer?) -> untyped } -> void
Cleared:
sig/datadog/di/serializer.rbs:24
└── def combine_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, Object target_self) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:25
└── def serialize_args: (Array[untyped] args, Hash[Symbol, untyped] kwargs, untyped instance_vars, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:26
└── def serialize_vars: (Hash[Symbol, untyped] vars, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:27
└── def serialize_value: (any value, ?name: (Symbol | String)?, ?depth: Integer, ?attribute_count: Integer?, ?length: Integer?, ?collection_size: Integer?, ?type: Class?) -> Hash[Symbol, untyped]
sig/datadog/di/serializer.rbs:30
└── def self.register: (?condition: Proc?) {
        (Serializer serializer, any value, name: ::Symbol?, depth: ::Integer, ?attribute_count: ::Integer?) -> untyped } -> void

Untyped other declarations

This PR introduces 1 partially typed other declaration, and clears 1 partially typed other declaration. It increases the percentage of typed other declarations from 84.91% to 84.93% (+0.02%).

Partially typed other declarations (+1-1)Introduced:
sig/datadog/di/serializer.rbs:9
└── @@flat_registry: ::Array[{condition: ::Proc?, proc: ^(Serializer serializer, any value, name: ::Symbol?, depth: ::Integer, ?attribute_count: ::Integer?) -> untyped}]
Cleared:
sig/datadog/di/serializer.rbs:8
└── @@flat_registry: ::Array[{condition: ::Proc?, proc: ^(Serializer serializer, any value, name: ::Symbol?, depth: ::Integer, ?attribute_count: ::Integer?) -> untyped}]

If you believe a method or an attribute is rightfully untyped or partially typed, you can add # untyped:accept on the line before the definition to remove it from the stats.

@p-datadog p-datadog changed the title Redact sensitive data in DI log-probe messages APMSP-3552 Redact sensitive data in DI log-probe messages Aug 5, 2026
@p-datadog
p-datadog marked this pull request as ready for review August 5, 2026 16:38
@p-datadog

Copy link
Copy Markdown
Member Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Dynamic Instrumentation’s log-probe message serialization to apply the same redaction decisions already used for snapshot capture, preventing message templates from leaking sensitive values by explicitly referencing them.

Changes:

  • Add a dedicated [redacted] placeholder for log-probe message serialization.
  • Redact message output when a value’s type matches redaction configuration.
  • Redact message output for Hash values / object instance variables when the identifier matches redaction configuration.
  • Add unit test cases covering redaction behavior in #serialize_value_for_message.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
lib/datadog/di/serializer.rb Adds message-path redaction logic for redacted types and identifiers in serialize_value_for_message.
spec/datadog/di/serializer_spec.rb Adds unit coverage for redaction behavior in message serialization.
sig/datadog/di/serializer.rbs Exposes the new REDACTED_VALUE_FOR_MESSAGE constant in the RBS signature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/datadog/di/serializer.rb Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a9189c268

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/datadog/di/serializer.rb
Comment thread lib/datadog/di/serializer.rb Outdated
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Aug 5, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 16.67%
Overall Coverage: 90.00% (-0.04%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: c0328cc | Docs | Datadog PR Page | Give us feedback!

@pr-commenter

pr-commenter Bot commented Aug 5, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-08-05 23:50:45

Comparing candidate commit c0328cc in PR branch di-message-serializer-redaction with baseline commit 5bd4a58 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 48 metrics, 1 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:tracing - trace.to_digest - Continue

  • unstable throughput [-1481.883op/s; +1523.972op/s] or [-5.039%; +5.182%]

serialize_value_for_message only redacted values discovered while walking
a structure (redacted types, hash keys, instance-variable names). A template
that referenced a redacted identifier directly ({password}, {@password},
{obj.password}, {hash['password']}) still emitted the plaintext value,
whereas the snapshot path redacts the equivalently-named local/ivar/key.

Thread the referenced identifier from each template segment through to the
serializer: Compiler#redaction_identifier returns the top-level referenced
name (ref/iref var, getmember field, index string key), Expression carries
it, and serialize_value_for_message redacts when that name is a redacted
identifier -- mirroring serialize_value on the snapshot path.

Verified: bundle exec rspec on serializer, compiler, probe_builder and
probe_notification_builder specs (new cases pass). Steep not run (tooling
gemfile targets Ruby 3.4/4.0; this container is 3.2).
…e messages

Address review comments (Copilot, codex): serialize_value_for_message ran
redactor.redact_identifier?(key) on every hash key. redact_identifier? sends
the key through Redactor#normalize, which calls #to_s. For non-String/Symbol
keys this invokes customer-defined #to_s -- running customer code (which DI
must not do) and, when #to_s raises, turning the whole message into a
serialization error.

Only String/Symbol keys can name a redacted identifier, so guard the check on
key type. Redaction for String/Symbol keys is unchanged; the value under a
non-String/Symbol key is still type-redacted when serialized.

- Fixed in lib/datadog/di/serializer.rb (hash-key branch of
  serialize_value_for_message)
- Added coverage in spec/datadog/di/serializer_spec.rb: a hash whose key is a
  non-String/Symbol object with a raising #to_s now serializes as
  "{... => value}" without invoking #to_s

Verified: bundle exec rspec on serializer, probe_notification_builder, compiler
and probe_builder specs (libdatadog_api compiled) -- 219 examples, 0 failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos debugger Live Debugger (+Dynamic Instrumentation, +Symbol Database)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants