fix(otel): apply mask function to object metadata attributes#835
fix(otel): apply mask function to object metadata attributes#835i-anubhav-anand wants to merge 2 commits into
Conversation
applyMaskInPlace only masked exact-key attributes (`maskCandidate in
span.attributes`). Object metadata is flattened into `${candidate}.${key}`
attributes, so object metadata was never masked — e.g. metadata
{ apiKey: "secret-123" } was exported verbatim despite a configured mask,
leaking secrets. (input/output mask fine because they use the bare key;
masking only worked for the uncommon primitive-metadata case.)
Scan attribute keys by prefix (mirroring MediaService.process, which walks
the same attribute list right after masking) so flattened metadata keys are
masked too. Adds an integration test (fails before: apiKey exported as
"secret-123").
|
@i-anubhav-anand is attempting to deploy a commit to the langfuse Team on Vercel. A member of the Team first needs to authorize it. |
| const maskRelevantAttributeKeys = Object.keys(span.attributes).filter( | ||
| (attributeName) => attributeName.startsWith(maskCandidate), | ||
| ); |
There was a problem hiding this comment.
The prefix check
startsWith(maskCandidate) does not require a separator, so it would also match an attribute whose name happens to begin with the same string by coincidence — for example, a future langfuse.observation.metadata_schema key would be silently masked. The same gap exists in MediaService.ts where this pattern was adopted from. A precise check of either an exact match or a dot-continuation is safer and future-proof.
| const maskRelevantAttributeKeys = Object.keys(span.attributes).filter( | |
| (attributeName) => attributeName.startsWith(maskCandidate), | |
| ); | |
| const maskRelevantAttributeKeys = Object.keys(span.attributes).filter( | |
| (attributeName) => | |
| attributeName === maskCandidate || | |
| attributeName.startsWith(maskCandidate + "."), | |
| ); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/otel/src/span-processor.ts
Line: 526-528
Comment:
The prefix check `startsWith(maskCandidate)` does not require a separator, so it would also match an attribute whose name happens to begin with the same string by coincidence — for example, a future `langfuse.observation.metadata_schema` key would be silently masked. The same gap exists in `MediaService.ts` where this pattern was adopted from. A precise check of either an exact match or a dot-continuation is safer and future-proof.
```suggestion
const maskRelevantAttributeKeys = Object.keys(span.attributes).filter(
(attributeName) =>
attributeName === maskCandidate ||
attributeName.startsWith(maskCandidate + "."),
);
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Adopted — switched to an exact-or-dot-prefix match (name === candidate || name.startsWith(candidate + ".")) so coincidental keys like ${candidate}_schema aren't masked. Thanks!
Address review feedback: startsWith(maskCandidate) could match a coincidental
key like `${candidate}_schema`. Match the candidate itself or its dot-prefixed
children instead.
A configured
maskfunction is never applied to object metadata, so secrets in metadata are exported verbatim.applyMaskInPlaceonly masks exact-key attributes:But object metadata is flattened into
${candidate}.${key}attributes (e.g.langfuse.observation.metadata.apiKey), which the bare-keyincheck never matches. So:input/outputmask correctly (they use the bare key); only the uncommon primitive-metadata path was masked.Fix scans attribute keys by prefix — mirroring
MediaService.process, which walks the same attribute list immediately after masking usingstartsWith. Now flattened metadata keys are masked too.Test
Adds an integration test masking object metadata (
{ apiKey: "secret-123" }→"***-123"). Fails before (exported as"secret-123"), passes after. All existing mask tests still pass;prettier/eslint/tscclean (pre-commit).Type of change
Greptile Summary
This PR fixes a bug where a configured
maskfunction was never applied to object-valued metadata: because metadata objects are flattened into${candidate}.${key}span attributes (e.g.,langfuse.observation.metadata.apiKey), the previous exact-keyincheck inapplyMaskInPlacenever matched them, so the values leaked verbatim. The fix replaces the exact-key lookup with a prefix scan — identical to the existing pattern inMediaService.process.span-processor.ts: Replacesif (maskCandidate in span.attributes)with anObject.keysprefix filter, masking both the bare key (primitive metadata) and all flattened child keys (object metadata).{ apiKey: "secret-123" }metadata, runs the mask function, and asserts each flattened attribute receives the transformed value.Confidence Score: 4/5
Safe to merge — the change is a two-line fix confined to a single private method, the logic is correct, and the new integration test validates the full round-trip.
The fix is correct and well-tested, but the
startsWithprefix match without a dot separator is a loose pattern inherited fromMediaService— a future attribute whose name starts with the same string would be silently masked. No current attribute conflicts exist, so this is a robustness concern rather than a present defect.No files require special attention; the single-method change in
span-processor.tsis straightforward and self-contained.Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(otel): apply mask function to object..." | Re-trigger Greptile