Skip to content

Generated Rust client leaks secrets via #[instrument] — add secrecy::SecretString support #169

Description

@avkonst

Root Cause:

The Rust client codegen emits #[instrument] on endpoint functions. Request structs containing password fields are logged by tracing as function arguments. Since codegen maps all opaque string-like types to plain String, the Debug impl prints passwords in cleartext to logs.

On the server side, secret::SecretString correctly redacts on Debug. But the generated client has no awareness of this — the field is just String, and #[instrument] happily prints it.

Solution:

Add first-class support for secrecy::SecretString in reflectapi, following the same pattern as uuid::Uuid and chrono::DateTime:

reflectapi crate: Add secrecy feature flag. Implement reflectapi::Input + reflectapi::Output for secrecy::SecretString. Registers as primitive "secrecy::SecretString".

Rust codegen: Recognize "secrecy::SecretString" → emit secrecy::SecretString in generated structs. Add secrecy dep to generated Cargo.toml. Since secrecy::SecretString implements Debug as SecretString([REDACTED]), #[instrument] no longer leaks values.

OpenAPI codegen: Map to { type: "string", format: "password" } (standard OpenAPI convention — tools like Swagger UI mask the input).

Python codegen: Map to pydantic.SecretStr (redacts on repr()/str()).

TypeScript codegen: Map to string (no runtime debug concern in TS; optionally a branded SecretString type for documentation).

Migration for private codebases:

Private secret::SecretString impls should set their reflectapi type to fall back to the public type. In the private Input impl for secret::SecretString, register as "secrecy::SecretString" (or use #[reflectapi(type = "secrecy::SecretString")]). This ensures the schema carries the public type name, and all codegen backends emit the correct secret-safe type.

Related: secret::SecretString is not capable of reflectapi::Output and a bunch of api responses *.signin return tokens as bare strings. These would be better converted to secret strings as well

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions