fix: Escape attribute names reported in redactedAttributes - #228
Open
jsonbailey wants to merge 3 commits into
Open
jsonbailey wants to merge 3 commits into
jsonbailey wants to merge 3 commits into
Conversation
When a whole attribute is redacted because allAttributesPrivate is set, or because the context is anonymous and anonymous redaction is enabled, the raw attribute name was reported in _meta.redactedAttributes. A name that starts with a slash, such as "/ssn", was therefore reported as "/ssn", which a consumer reads as a path to a nested property "ssn". The redaction of the top-level attribute was not communicated. It is now reported as the escaped attribute reference "/~1ssn". Only the reported reference was wrong. The attribute value was never sent.
The depth-1 private-reference match already reports the configured reference, which AttributeRef always stores in escaped form. This case guards that behavior for a per-context private attribute.
jsonbailey
marked this pull request as ready for review
September 29, 2026 20:46
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
When an attribute is redacted wholesale, the SDK reported the raw attribute name in
_meta.redactedAttributesinstead of an escaped attribute reference. A literal attribute named/ssnwas reported as/ssn, which a consumer parses as a path to a nested propertyssn— so the redaction of the top-level/ssnattribute was never communicated.No attribute value was ever leaked. Only the reported reference string was wrong. The correct form is
/~1ssn.This affected the two paths that redact a whole attribute without consulting a configured reference:
allAttributesPrivateis setThe fix
In
writeOrRedactAttribute, wrap the name before reporting it:AttributeRefis unchanged — it was already correct.fromLiteralescapes a leading slash (/ssn→/~1ssn,/a~b→/~1a~0b) and leaves any other name alone, since a name without a leading slash is already a valid reference. That behavior already had test coverage inAttributeRefTest.literal().The two duplicate branches were also collapsed into one condition, since both arms did the same thing.
Why only one call site changed
There are three
addOrCreate(redacted, ...)sites inEventContextFormatter:addOrCreate(redacted, "name")"name"can never need escapingwriteOrRedactAttributewriteRedactedValue, depth-1 private matchprivateRef.toString()Line 136 handles the explicitly-configured-private case and was never broken: it reports the configured reference rather than the attribute name, and
AttributeRefalways stores itsrawPathpre-escaped regardless of how it was built.Note for reviewers on the test count: four test cases were added, but only two were failing beforehand. The two covering explicitly-configured private attributes (
fromLiteral("/ssn")as a global private attribute, and.privateAttributes("/~1ssn")per-context) pass against the pre-fix source as well — they are regression guards for the already-correct line 136, not evidence of a second fix.Test evidence
lib/shared/internal: 318 tests pass, 0 failures,checkstyleMainandcheckstyleTestclean.Revert check: restoring the pre-fix
EventContextFormatter.javawhile keeping the new tests fails exactly the two new escaping cases, with the raw-vs-escaped difference explicit:EventContextFormatterTestalso gained aredactAnonymousparameter. It previously hardcodedwrite(context, jw, false), so the anonymous-redaction branch had no direct coverage in this formatter's own test.Not verifiable in CI yet
The 14 contract tests in #203 cannot go green from this branch.
lib/sdk/server/build.gradle:74pinslaunchdarklyJavaSdkInternal = "1.11.1"and line 115 consumes it as a published Maven artifact — the server SDK does not buildlib/shared/internalfrom source. Running the contract tests against this branch would exercise the released 1.11.1, not this change.Going green requires a
java-sdk-internalrelease followed by a version bump in the server SDK. Neither is done here, since version changes belong to the release process.Prior art
The same defect was fixed in two sibling SDKs:
Python additionally had to fix its
from_literalhelper, which did not escape a leading slash. Java'sfromLiteralwas already correct, so no equivalent change was needed. .NET needed two call-site fixes because itsWriteOrRedactshort-circuits with a raw name before reaching the equivalent ofwriteRedactedValue; Java falls through toprivateRef.toString(), which is why one fix suffices here.SDK-3216
Note
Overview
Fixes
_meta.redactedAttributesso wholesale redaction (global all attributes private or anonymous contexts when anonymous redaction is on) records escaped attribute references instead of raw names.In
writeOrRedactAttribute, redacted entries now useAttributeRef.fromLiteral(attrName).toString()(e.g./ssn→/~1ssn), so consumers do not misread a top-level slash-prefixed attribute as a nested path. The two identical redaction branches are merged into one condition.EventContextFormatterTestadds aredactAnonymousparameter (tests previously always passedfalse) and new cases for slash/~attribute names, anonymous redaction, and configured-private slash-prefixed attributes as regression guards.Reviewed by Cursor Bugbot for commit 3011d66. Bugbot is set up for automated code reviews on this repo. Configure here.