mlevkov opened a new issue, #3801:
URL: https://github.com/apache/iggy/issues/3801

   ## Summary
   
   The connectors guidance tells plugin authors that annotating a 
`SecretString` field with `iggy_common::serde_secret::serialize_secret` gives 
them redaction. It does the opposite: the helper calls `expose_secret()` and 
writes the value in plaintext. Nine connector plugins follow that guidance 
today.
   
   ## The claim
   
   `.claude/skills/connectors-overview/SKILL.md`, "Secrets":
   
   > `secrecy::SecretString` + `iggy_common::serde_secret::serialize_secret` | 
`sinks/postgres_sink::PostgresSinkConfig::connection_string` | **Auto-redact on 
Debug/Display + serialization**
   
   ## The code
   
   `core/common/src/utils/serde_secret.rs`:
   
   ```rust
   pub fn serialize_secret<S: serde::Serializer>(
       secret: &SecretString,
       serializer: S,
   ) -> Result<S::Ok, S::Error> {
       serializer.serialize_str(secret.expose_secret())
   }
   ```
   
   Its own module doc says the opposite of the skill:
   
   > These helpers are for fields that **must** be serialized (e.g., wire 
protocol payloads, persisted TOML configs, API responses that already expose 
credentials by design).
   >
   > Do **not** add `serialize_with` to fields that should remain redacted in 
serialized output — rely on `SecretString`'s default behavior instead.
   
   And its test `serialize_optional_secret_with_some_value` asserts the output 
is `{"token":"tok_123"}`.
   
   The `Debug` half of the claim is true — `SecretBox`'s `Debug` prints 
`[REDACTED]`. Only the serialization half is inverted.
   
   ## Why it matters
   
   `SecretString` deliberately has no `Serialize` impl, so a config struct 
holding one cannot derive `Serialize` at all. Adding `serialize_with` is what 
*unblocks* the derive — it converts a compile-time guarantee into plaintext 
output, while the documentation says it does the reverse.
   
   Nine plugins carry the annotation on credential fields:
   
   `sinks/{postgres,s3,influxdb,elasticsearch,mongodb,surrealdb}_sink`, 
`sources/{postgres,elasticsearch,influxdb}_source`.
   
   In practice the attribute appears inert: the runtime keeps plugin 
configuration as `serde_json::Value` and never deserializes into a plugin's 
config struct, so nothing calls these serializers today. The problem is that 
the protection authors believe they have does not exist, and the first caller 
that serializes a plugin config leaks every credential in it.
   
   ## Suggested fix
   
   1. Correct the SKILL.md row — the helper exposes; `SecretString`'s default 
(no `Serialize` impl) is what redacts.
   2. Consider dropping `Serialize` from plugin config structs that do not need 
it. `iggy_connector_http_source` (#3798) does this, which makes the property 
compiler-enforced rather than convention-enforced.
   3. If a config struct genuinely needs `Serialize`, a `serialize_redacted` 
helper alongside the existing one would give authors the thing the docs 
currently promise.
   
   Found while preparing #3798.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to