Skip to content

fix(codegen/rust): stop recording request bodies in instrumented client spans#173

Merged
hardbyte merged 1 commit into
mainfrom
fix/instrument-skip-request-body
Jun 12, 2026
Merged

fix(codegen/rust): stop recording request bodies in instrumented client spans#173
hardbyte merged 1 commit into
mainfrom
fix/instrument-skip-request-body

Conversation

@hardbyte

@hardbyte hardbyte commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Part of #169 (just the mitigation); first-class secrecy::SecretString support (type mapping across all backends) remains as a follow-up.

Problem

Instrumented Rust clients emit:

#[tracing::instrument(name = "/auth.login", skip(self, headers))]
pub async fn login(&self, input: LoginRequest, headers: Empty) -> ...

input is not skipped, so tracing records the entire request body as a span field via its Debug impl. Since codegen maps string-like types to plain String, credentials print to logs in cleartext.

Fix

The generated attribute now uses skip_all. No function arguments are recorded; the span keeps the endpoint path as its name. This protects every request field — including secrets typed as plain String on the server — which first-class SecretString support alone would not.

Trade-off: request bodies no longer appear as span fields at all. Given the payload is arbitrary user data with no redaction story, recording it by default was a liability rather than a feature.

Tests & validation

  • New test instrumented_rust_client_does_not_record_request_bodies generates the demo schema with .instrument(true) and asserts every emitted #[tracing::instrument] attribute uses skip_all (and that attributes are emitted at all).
  • The committed demo Rust client is regenerated (8 endpoints switch to skip_all) and compiles as a workspace member.
  • Empirically validated with a scratch crate replicating the exact generated method shape under a captured tracing-subscriber:
    • old attribute: INFO /auth.login{input=LoginRequest { username: "brian", password: "hunter2-s3cret" }}: sending request — password in cleartext
    • new attribute: INFO /auth.login: sending request — nothing recorded, span name retained
  • Full demo suite passes (195/195); fmt/clippy/build clean.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

📖 Documentation Preview: https://reflectapi-docs-preview-pr-173.partly.workers.dev

Updated automatically from commit 9cf7e9a

…nt spans

Instrumented Rust clients emitted #[tracing::instrument(name = ..., skip(self, headers))], which records the input argument as a span field via its Debug impl. Request bodies can carry credentials, so passwords and tokens reached logs in cleartext.

Switch the generated attribute to skip_all: no function arguments are recorded, while the span keeps the endpoint path as its name. This protects every field, including secrets typed as plain String, independently of future first-class secrecy::SecretString support.

Part of #169 (the secrecy::SecretString type mapping remains as follow-up).
@hardbyte hardbyte force-pushed the fix/instrument-skip-request-body branch from fba10cb to b4045ab Compare June 11, 2026 23:52
@avkonst

avkonst commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

I thought about doing this but then concluded the better more complete fix would be to tell via reflectapi attribute per struct or per field what to include into the instrumentation. But that is bigger change so I parked if for now.
Do you want to merge it to close the leak of credentials in traces?

@hardbyte hardbyte merged commit 8857cf2 into main Jun 12, 2026
6 checks passed
@hardbyte hardbyte deleted the fix/instrument-skip-request-body branch June 12, 2026 02:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants