Skip to content

[Debugger] Redact values of redacted types in expression member and index access - #9539

Draft
dudikeleti wants to merge 3 commits into
dudik/fix/debugger-redacted-metric-no-throwfrom
dudik/fix/debugger-redacted-type-member-access
Draft

dudikeleti wants to merge 3 commits into
dudik/fix/debugger-redacted-metric-no-throwfrom
dudik/fix/debugger-redacted-type-member-access

Conversation

@dudikeleti

@dudikeleti dudikeleti commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary of changes

  • Values of a type configured as redacted (DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES) are now redacted wherever an expression uses them, not only when dumped whole. That covers ref, getmember, index, this, @return, @it, @key and @value, and the declared type of the member or item being read.
  • Compiler-generated field names, such as <password>k__BackingField, primary-constructor captures, anonymous-type fields and hoisted locals, are matched against redaction keywords by their source name.

Reason for change

  • {"getmember":[{"ref":"customPii"},"TestValue"]} returned the field of a redacted type. MemberPathExpression checked only the member name; the type it passed was the type of the name string. Conditions such as eq(getmember(customPii,"key"),"guess") worked as an oracle.
  • Compiler-generated names normalize to non-keywords, so getmember(pii, "<password>k__BackingField") bypassed keyword redaction.

Implementation details

  • Everything is decided when the expression compiles; nothing is added per evaluation. Expressions are compiled per runtime type of this, the return value and every argument and local, so for those roots the static type is the exact runtime type.
  • A redacted value becomes the redaction placeholder and adds a redaction error. Operations whose operand is the placeholder (any, all, filter, isEmpty, len, index, string operations, comparisons, logical operators) return the placeholder or false instead of adding a non-redaction error, which would replace the rendered template as the snapshot message.
  • A condition that uses a redacted value compiles to false; its redaction error sets HasConditionError, as before.
  • ShouldRedactValue (moved from the dump partial to the general one) is shared by dumps and member access.

Behavior changes:

  • {"ref":"x"} for a redacted-type local still renders {redacted} and now also reports a redaction error.
  • When this is a redacted type, its fields, {"ref":"this"} and conditions on it are redacted.
  • A template or capture that iterates a collection whose element type is redacted, and reads @it, renders {redacted}.
  • getmember no longer redacts because the type of the member-name string is configured as redacted.

Test coverage

  • A nested RedactedTypeTests class (in the non-parallel RedactionTests collection) covers ref, getmember, index, @it, @key, @value, this, conditions, collection and string operations over a redacted value, and non-redacted types.
  • Compiler-generated field names in templates and snapshot messages.
  • One test pins the remaining gap below.
  • dotnet test tracer/test/Datadog.Trace.Tests/Datadog.Trace.Tests.csproj -c Release -f net10.0 --filter "FullyQualifiedName~Datadog.Trace.Tests.Debugger": 962 passed, 2 skipped.
  • Same with -f net48: 926 passed, 29 skipped.

Other details

@pr-commenter

pr-commenter Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-10-29 15:33:20

Comparing candidate commit 642af28 in PR branch dudik/fix/debugger-redacted-type-member-access with baseline commit a5e0b80 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 72 metrics, 0 unstable metrics, 7 known flaky benchmarks, 119 flaky benchmarks without significant changes.

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 ----------------------------------'

Known flaky benchmarks

These benchmarks are marked as flaky and will not trigger a failure. Modify FLAKY_BENCHMARKS_REGEX to control which benchmarks are marked as flaky.

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest netcoreapp3.1

  • unstable execution_time [-107.460ms; -51.623ms] or [-21.610%; -10.381%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • unstable execution_time [-102.655ms; -17.218ms] or [-18.747%; -3.144%]
  • unstable throughput [-48.953op/s; +29.295op/s] or [-8.601%; +5.147%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice netcoreapp3.1

  • unstable throughput [-58.556op/s; -10.562op/s] or [-14.946%; -2.696%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net6.0

  • 🟥 execution_time [+51.642µs; +128.522µs] or [+5.274%; +13.125%]
  • 🟥 throughput [-141.967op/s; -59.897op/s] or [-13.667%; -5.766%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark netcoreapp3.1

  • 🟩 allocated_mem [-17.565KB; -17.542KB] or [-6.459%; -6.451%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark net6.0

  • unstable execution_time [-3974.884ns; +2462.884ns] or [-7.316%; +4.533%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark netcoreapp3.1

  • unstable execution_time [-4191.242ns; +3563.242ns] or [-7.532%; +6.404%]

Known flaky benchmarks without significant changes:

  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1
  • scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net472
  • scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net6.0
  • scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild netcoreapp3.1
  • scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net472
  • scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net6.0
  • scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs net472
  • scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net472
  • scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack netcoreapp3.1
  • scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net472
  • scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net6.0
  • scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net6.0
  • scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net6.0
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net6.0
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice netcoreapp3.1
  • scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net472
  • scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net6.0
  • scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch netcoreapp3.1
  • scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net472
  • scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net6.0
  • scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync netcoreapp3.1
  • scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net472
  • scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net6.0
  • scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync netcoreapp3.1
  • scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net472
  • scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net6.0
  • scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog netcoreapp3.1
  • scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark net472
  • scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark net6.0
  • scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark net472
  • scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net472
  • scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net6.0
  • scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog netcoreapp3.1
  • scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net472
  • scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net6.0
  • scenario:Benchmarks.Trace.RedisBenchmark.SendReceive netcoreapp3.1
  • scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net472
  • scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net6.0
  • scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog netcoreapp3.1
  • scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net472
  • scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net6.0
  • scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore netcoreapp3.1
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net6.0
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope netcoreapp3.1
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net6.0
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan netcoreapp3.1
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net6.0
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes netcoreapp3.1
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net472
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net6.0
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin netcoreapp3.1

@dudikeleti
dudikeleti force-pushed the dudik/fix/debugger-redacted-metric-no-throw branch from 17767da to 9db5c69 Compare October 28, 2026 10:18
@dudikeleti
dudikeleti force-pushed the dudik/fix/debugger-redacted-type-member-access branch from aae68b5 to 809ede3 Compare October 28, 2026 10:18
…ndex access

Check the static type of ref, getmember, index and the this, @return,
@exception, @it, @key and @value identifiers at compile time, and redact
both the source and the resolved member or item when the type is
configured as redacted. Match auto-property backing-field names against
redaction keywords using the helper the dump path already uses.
Collection, string, comparison and logical operations whose operand is
the redacted placeholder now return the placeholder or false at compile
time instead of adding a non-redaction error, which would replace the
rendered template as the snapshot message. Predicates over a redacted
source are parsed only to keep the reader aligned, and their errors are
dropped.
…ames

Match the source name of any compiler-generated field, such as primary
constructor captures, anonymous type fields and hoisted locals, against
redaction keywords. Short-circuit len and the index, key, substring bound
and string argument operands on the redacted placeholder, and compile a
condition that uses a redacted value to false. Share the redaction and
filter-state helpers.
@dudikeleti
dudikeleti force-pushed the dudik/fix/debugger-redacted-metric-no-throw branch from 9db5c69 to 93a16cf Compare October 29, 2026 14:19
@dudikeleti
dudikeleti force-pushed the dudik/fix/debugger-redacted-type-member-access branch from 809ede3 to 642af28 Compare October 29, 2026 14:19
@dd-trace-dotnet-ci-bot

Copy link
Copy Markdown

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (9539) and master.

✅ No regressions detected

📄 View the full report (charts + all metrics) →

dudikeleti added a commit that referenced this pull request Oct 30, 2026
## Summary of changes

Adds Dynamic Instrumentation rules to the `/sdk-review` lens files, so
the guardrails in `DebuggerSafetyBoundaries.md` are checked before every
push:

- `Security-And-Safety.md`: section 3 is now "Dynamic Instrumentation".
SEC-3.1 also covers expression-tree property access and static property
getters. New rules SEC-3.2 to SEC-3.6 cover runtime-type checks,
dictionary keys and compiler-generated names, values computed from
redacted values, customer data in evaluation errors and logs, and
`Expression.Equal` null checks.
- `Performance.md`: new section 5 with PERF-5.1 and PERF-5.2 for per-hit
locks and cache hits, and failure output on every hit.
- `Correctness.md`: COR-6.4 now says which time bounds are acceptable in
tests, flags raising a timeout to fix a flaky test, and limits queued
markers to single-consumer queues. This replaces #9414.

## Reason for change

The guardrails added to `DebuggerSafetyBoundaries.md` in #9562 are rules
we want enforced, and `/sdk-review` is what runs before every push.
Keeping them in the lens files instead of `AGENTS.md` keeps `AGENTS.md`
small.

Each new Dynamic Instrumentation rule comes from a bug fixed in a recent
PR, and the COR-6.4 change comes from #9414:

| Rule | Fixed in |
| --- | --- |
| SEC-3.1 (update) | #8878 |
| SEC-3.2 runtime type | #9483, #9541, #9547 |
| SEC-3.3 dictionary keys and source names | #9536, #9539, #9548 |
| SEC-3.4 computed from redacted values | #9485, #9537, #9547 |
| SEC-3.5 errors and logs | #9559 |
| SEC-3.6 `Expression.ReferenceEqual` | #9541 |
| PERF-5.1 per-hit locks, cache hits | #9538 |
| PERF-5.2 failure output on every hit | #9536, #9537, #9559 |
| COR-6.4 (update) | from #9414, not a bug fix |

## Implementation details

SEC-3.1 keeps its ID; the section heading changes from "Customer-process
safety" to "Dynamic Instrumentation". The Performance "Don't flag" entry
about uncontended per-request locks now points to PERF-5.1, so the two
don't conflict for locks shared by every thread that hits a probe.

SEC-3.1 still links to `DebuggerSafetyBoundaries.md` for the allowed
execution policy. None of these rules depend on #9562, but #9562 points
the guide's review checklist to them, so merge this PR first.

## Test coverage

Docs only. As in #9472, I ran the rules against commits that introduced
the issues. Each run was one independent lens reviewer reading the
frozen commit and only its lens file. No reviewer was told which rule
was being tested.

| Rule | Commit reviewed | Result |
| --- | --- | --- |
| SEC-3.2 | #8879 | Caught: `ShouldRedactDumpValue(null,
expression.Type)` in `ProbeExpressionParser.Dump.cs` decides redaction
from the declared type |
| SEC-3.4 | #8621 (product code) | Caught: `len(ref password)` and
`eq(ref password, "x")` capture values computed from the redaction
placeholder |
| SEC-3.5 | #8621 (product code) | Caught: capture expression errors
send `e.Message` in `evaluationErrors` |
| SEC-3.6 | #5543 (product code) | Caught: the
`Expression.Equal(expression, Expression.Constant(null))` null check |
| PERF-5.1 | #6292 | Caught: every redaction cache hit takes a
`ReaderWriterLockSlim` read lock and the LFU policy's write lock, and
allocates |
| PERF-5.1 | #9538 | Correctly not flagged (the lock-free fix) |
| PERF-5.2 | #8725 | Missed: the uncapped metric probe path isn't in
that diff |
| PERF-5.2 | #8621 (product code) | Flagged a different per-hit failure
log in the same block (`Evaluation result should not be null`), not the
`Log.Error` in the catch |
| COR-6.4 | #9396 | Caught: the flush timeout raised from 5s to 1 minute
to fix a flaky test |
| COR-6.4 | #9074 | Correctly not flagged (`TaskCompletionSource` with a
30s guard) |

Not tested: SEC-3.1's new expression-tree clause and SEC-3.3. Reviewers
did point out SEC-3.3's literal-only dictionary key check in code that
predates the commits under review.

## Other details

I kept only rules a reviewer can match in a diff and that a PR shows
were needed. An earlier version had a PERF-5.3 rule for
`EvaluationBudget` checks. I dropped it because no PR shows a missed
check, and it mostly explained how the budget works; that guidance stays
in `DebuggerSafetyBoundaries.md` (#9562). SEC-3.3 was also narrowed to
the two shapes a diff shows: dictionary keys checked only when literal,
and keywords matched against compiler-generated names.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant