[Debugger] Redact dictionary values by sensitive keys#8768
Conversation
Signed-off-by: dudik <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR updates the debugger snapshot serializer to ensure dictionary values are redacted when their corresponding dictionary keys match sensitive identifiers (e.g., "password"), preventing secret leakage in snapshots.
Changes:
- Introduces a shared
WriteRedactedValuehelper to emit the standard redacted JSON shape. - Applies redaction during dictionary entry serialization by checking the dictionary key (string) and emitting a redacted value instead of serializing the runtime value.
- Adds a unit test to validate redaction for both
Dictionary<string, object>andDictionary<object, object>when the key is sensitive.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tracer/src/Datadog.Trace/Debugger/Snapshots/DebuggerSnapshotSerializer.cs | Adds WriteRedactedValue helper and redacts dictionary entry values based on sensitive string keys. |
| tracer/test/Datadog.Trace.Tests/Debugger/DebuggerSnapshotCreatorTests.cs | Adds a regression test asserting dictionary values are redacted when the key is sensitive. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <[email protected]> Signed-off-by: dudik <[email protected]>
Signed-off-by: dudik <[email protected]>
This comment has been minimized.
This comment has been minimized.
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8768) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:
Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard. Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph). Duration chartsFakeDbCommand (.NET Framework 4.8)gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (74ms) : 70, 78
master - mean (75ms) : 71, 78
section Bailout
This PR (8768) - mean (80ms) : 77, 84
master - mean (78ms) : 75, 80
section CallTarget+Inlining+NGEN
This PR (8768) - mean (1,111ms) : 1066, 1157
master - mean (1,103ms) : 1046, 1161
FakeDbCommand (.NET Core 3.1)gantt
title Execution time (ms) FakeDbCommand (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (113ms) : 109, 116
master - mean (115ms) : 110, 119
section Bailout
This PR (8768) - mean (116ms) : 110, 122
master - mean (115ms) : 111, 118
section CallTarget+Inlining+NGEN
This PR (8768) - mean (794ms) : 768, 820
master - mean (795ms) : 769, 821
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (102ms) : 99, 105
master - mean (103ms) : 98, 109
section Bailout
This PR (8768) - mean (103ms) : 100, 105
master - mean (102ms) : 100, 105
section CallTarget+Inlining+NGEN
This PR (8768) - mean (953ms) : 905, 1000
master - mean (952ms) : 914, 990
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (102ms) : 97, 107
master - mean (101ms) : 97, 106
section Bailout
This PR (8768) - mean (101ms) : 99, 103
master - mean (102ms) : 98, 105
section CallTarget+Inlining+NGEN
This PR (8768) - mean (827ms) : 787, 867
master - mean (825ms) : 790, 860
HttpMessageHandler (.NET Framework 4.8)gantt
title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (198ms) : 193, 203
master - mean (199ms) : 194, 203
section Bailout
This PR (8768) - mean (201ms) : 197, 206
master - mean (202ms) : 198, 206
section CallTarget+Inlining+NGEN
This PR (8768) - mean (1,190ms) : 1148, 1232
master - mean (1,189ms) : 1153, 1225
HttpMessageHandler (.NET Core 3.1)gantt
title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (285ms) : 281, 290
master - mean (286ms) : 280, 292
section Bailout
This PR (8768) - mean (285ms) : 281, 290
master - mean (288ms) : 281, 294
section CallTarget+Inlining+NGEN
This PR (8768) - mean (958ms) : 939, 978
master - mean (956ms) : 936, 976
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (279ms) : 273, 286
master - mean (275ms) : 270, 280
section Bailout
This PR (8768) - mean (276ms) : 270, 283
master - mean (276ms) : 270, 282
section CallTarget+Inlining+NGEN
This PR (8768) - mean (1,157ms) : 1126, 1188
master - mean (1,154ms) : 1108, 1200
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8768) - mean (277ms) : 270, 284
master - mean (274ms) : 268, 280
section Bailout
This PR (8768) - mean (278ms) : 271, 285
master - mean (275ms) : 268, 282
section CallTarget+Inlining+NGEN
This PR (8768) - mean (1,039ms) : 997, 1081
master - mean (1,036ms) : 997, 1075
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
BenchmarksBenchmark execution time: 2026-06-09 13:14:07 Comparing candidate commit 0b833a6 in PR branch Found 0 performance improvements and 1 performance regressions! Performance is the same for 71 metrics, 0 unstable metrics, 59 known flaky benchmarks, 67 flaky benchmarks without significant changes.
|
a09b0d4 to
0b833a6
Compare
### Motivation - A change started serializing runtime scalar values for object-typed dictionary entries without supplying the dictionary key as the variable name, which allowed sensitive entries (e.g., `"password"`) to bypass redaction and leak secrets in debugger snapshots. ### Description - Add a shared helper `WriteRedactedValue` to emit the redacted JSON shape and use it from `SerializeInternal` when `Redaction.Instance.ShouldRedact(...)` returns true for variables. - During dictionary entry serialization, check string dictionary keys with `Redaction.Instance.IsRedactedKeyword(...)` and use `WriteRedactedValue` for redacted values instead of serializing the runtime value. - This change applies to supported dictionary serialization when the runtime key is a string redacted identifier. Standalone `KeyValuePair` objects and non-string dictionary keys remain out of scope. - Add a unit test `ObjectStructure_DictionaryEntryValue_WithRedactedKey_IsRedacted` covering `Dictionary<string, object>`, `Dictionary<object, object>`, and `Hashtable` to ensure entries under a sensitive key are emitted with `notCapturedReason` and the secret value is not included. ### Testing - Ran the focused unit tests with `dotnet test tracer/test/Datadog.Trace.Tests/Datadog.Trace.Tests.csproj -f net8.0 --filter "FullyQualifiedName~DebuggerSnapshotCreatorTests.ObjectStructure_DictionaryEntryValue_WithRedactedKey_IsRedacted|FullyQualifiedName~DebuggerSnapshotCreatorTests.ObjectStructure_ObjectTypedDictionaryEntries_UseRuntimeTypeAndNullFallback" -v minimal` and both tests passed. - Verified there are no `git diff --check` issues and added tests exercise the fixed behavior (redaction of dictionary values by key). ------ [Codex Task](https://chatgpt.com/codex/cloud/tasks/task_b_6a27d290b0e883279db47146c5a6fefc) --------- Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Motivation
"password") to bypass redaction and leak secrets in debugger snapshots.Description
WriteRedactedValueto emit the redacted JSON shape and use it fromSerializeInternalwhenRedaction.Instance.ShouldRedact(...)returns true for variables.Redaction.Instance.IsRedactedKeyword(...)and useWriteRedactedValuefor redacted values instead of serializing the runtime value.KeyValuePairobjects and non-string dictionary keys remain out of scope.ObjectStructure_DictionaryEntryValue_WithRedactedKey_IsRedactedcoveringDictionary<string, object>,Dictionary<object, object>, andHashtableto ensure entries under a sensitive key are emitted withnotCapturedReasonand the secret value is not included.Testing
dotnet test tracer/test/Datadog.Trace.Tests/Datadog.Trace.Tests.csproj -f net8.0 --filter "FullyQualifiedName~DebuggerSnapshotCreatorTests.ObjectStructure_DictionaryEntryValue_WithRedactedKey_IsRedacted|FullyQualifiedName~DebuggerSnapshotCreatorTests.ObjectStructure_ObjectTypedDictionaryEntries_UseRuntimeTypeAndNullFallback" -v minimaland both tests passed.git diff --checkissues and added tests exercise the fixed behavior (redaction of dictionary values by key).Codex Task