fix(config): redact secret fields in channel/A2A/MCP config Serialize - #6173
Merged
Conversation
bug-ops
enabled auto-merge (squash)
July 12, 2026 20:47
bug-ops
force-pushed
the
fix/6166-config-serialize-leak
branch
from
July 12, 2026 20:47
4b1cacc to
84bd2ea
Compare
bug-ops
force-pushed
the
fix/6166-config-serialize-leak
branch
from
July 12, 2026 20:52
84bd2ea to
791196e
Compare
TelegramConfig.token, DiscordConfig.token, SlackConfig.bot_token, and SlackConfig.signing_secret are vault-only fields, always None at every --init persist point, so a future diagnostic Serialize of a live Config (runtime-hydrated with real vault secrets) would have leaked them despite their Debug impls already redacting. Add #[serde(skip_serializing)] to each; Deserialize is untouched so inline tokens still load. DiscordConfig.application_id is a public snowflake, not a secret, and is left untouched. A2aServerConfig.auth_token, McpServerConfig.env, and McpServerConfig.headers legitimately hold raw values persisted to config.toml by --init, so their derived Serialize stays plaintext; each gets a doc note directing diagnostics to the redacting Debug impl instead. Closes #6166
bug-ops
force-pushed
the
fix/6166-config-serialize-leak
branch
from
July 12, 2026 21:01
791196e to
e1be1c5
Compare
bug-ops
added a commit
that referenced
this pull request
Jul 14, 2026
…eaking gateway auth token PiiFilterConfig and SecretMaskingConfig shipped enabled: false, and the --init wizard defaulted both prompts to disabled, so an operator accepting defaults ended up with neither protection active despite both existing specifically to keep vault secrets and PII out of LLM payloads, message history, and debug dumps. Flip both to enabled by default (field-level default_true, not just Default::default(), so the section-present-key- omitted case resolves correctly too) and fix the two shipped reference configs that still wrote an explicit enabled = false, which would have silently overridden the new default. GatewayConfig.auth_token is vault-hydrated in place at startup but only had #[serde(default)], not #[serde(skip_serializing)], unlike its already- fixed Telegram/Discord/Slack siblings (PR #6173) - any future diagnostic or config-export serialization of a live Config would have leaked the real gateway bearer token in plaintext. Closes #6263 Closes #6248
bug-ops
added a commit
that referenced
this pull request
Jul 14, 2026
…eaking gateway auth token PiiFilterConfig and SecretMaskingConfig shipped enabled: false, and the --init wizard defaulted both prompts to disabled, so an operator accepting defaults ended up with neither protection active despite both existing specifically to keep vault secrets and PII out of LLM payloads, message history, and debug dumps. Flip both to enabled by default (field-level default_true, not just Default::default(), so the section-present-key- omitted case resolves correctly too) and fix the two shipped reference configs that still wrote an explicit enabled = false, which would have silently overridden the new default. GatewayConfig.auth_token is vault-hydrated in place at startup but only had #[serde(default)], not #[serde(skip_serializing)], unlike its already- fixed Telegram/Discord/Slack siblings (PR #6173) - any future diagnostic or config-export serialization of a live Config would have leaked the real gateway bearer token in plaintext. Closes #6263 Closes #6248
bug-ops
added a commit
that referenced
this pull request
Jul 14, 2026
…eaking gateway auth token PiiFilterConfig and SecretMaskingConfig shipped enabled: false, and the --init wizard defaulted both prompts to disabled, so an operator accepting defaults ended up with neither protection active despite both existing specifically to keep vault secrets and PII out of LLM payloads, message history, and debug dumps. Flip both to enabled by default (field-level default_true, not just Default::default(), so the section-present-key- omitted case resolves correctly too) and fix the two shipped reference configs that still wrote an explicit enabled = false, which would have silently overridden the new default. GatewayConfig.auth_token is vault-hydrated in place at startup but only had #[serde(default)], not #[serde(skip_serializing)], unlike its already- fixed Telegram/Discord/Slack siblings (PR #6173) - any future diagnostic or config-export serialization of a live Config would have leaked the real gateway bearer token in plaintext. Closes #6263 Closes #6248
bug-ops
added a commit
that referenced
this pull request
Jul 14, 2026
…eaking gateway auth token PiiFilterConfig and SecretMaskingConfig shipped enabled: false, and the --init wizard defaulted both prompts to disabled, so an operator accepting defaults ended up with neither protection active despite both existing specifically to keep vault secrets and PII out of LLM payloads, message history, and debug dumps. Flip both to enabled by default (field-level default_true, not just Default::default(), so the section-present-key- omitted case resolves correctly too) and fix the two shipped reference configs that still wrote an explicit enabled = false, which would have silently overridden the new default. GatewayConfig.auth_token is vault-hydrated in place at startup but only had #[serde(default)], not #[serde(skip_serializing)], unlike its already- fixed Telegram/Discord/Slack siblings (PR #6173) - any future diagnostic or config-export serialization of a live Config would have leaked the real gateway bearer token in plaintext. Closes #6263 Closes #6248
bug-ops
added a commit
that referenced
this pull request
Jul 14, 2026
…eaking gateway auth token (#6295) PiiFilterConfig and SecretMaskingConfig shipped enabled: false, and the --init wizard defaulted both prompts to disabled, so an operator accepting defaults ended up with neither protection active despite both existing specifically to keep vault secrets and PII out of LLM payloads, message history, and debug dumps. Flip both to enabled by default (field-level default_true, not just Default::default(), so the section-present-key- omitted case resolves correctly too) and fix the two shipped reference configs that still wrote an explicit enabled = false, which would have silently overridden the new default. GatewayConfig.auth_token is vault-hydrated in place at startup but only had #[serde(default)], not #[serde(skip_serializing)], unlike its already- fixed Telegram/Discord/Slack siblings (PR #6173) - any future diagnostic or config-export serialization of a live Config would have leaked the real gateway bearer token in plaintext. Closes #6263 Closes #6248
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.
Summary
Closes the Serialize-leak sibling cohort of #6006/#6165.
TelegramConfig.token,DiscordConfig.token,SlackConfig.bot_token, andSlackConfig.signing_secretare alwaysNoneat every--initpersist point (the wizard sends real secrets to the vault, not the config field), so they are safe to strip fromSerializeentirely — a future diagnostic serialize of a live, runtime-hydratedConfigcould otherwise leak them despite the existing redactingDebugimpls.#[serde(skip_serializing)]closes that gap without touchingDeserialize.DiscordConfig.application_idis a public Discord snowflake, not a secret — left untouched.A2aServerConfig.auth_token,McpServerConfig.env, andMcpServerConfig.headerslegitimately hold raw values persisted toconfig.tomlby--init, so a blanket redactingSerialize(as used in #6165) would corrupt round-tripped configs; instead each field gets a# Securitydoc note directing any diagnostic output to the existing redactingDebugimpl.TelegramConfig.token,DiscordConfig.token,SlackConfig.bot_token,SlackConfig.signing_secret→#[serde(skip_serializing)]DiscordConfig.application_id→ untouched--initround-trip):A2aServerConfig.auth_token,McpServerConfig.env,McpServerConfig.headers→ doc note onlyThree sibling fields with the identical latent pattern (
GatewayConfig.auth_token,AcpConfig.auth_token, and the A2A wizard's raw-token-to-config.toml write) were identified during the audit and intentionally left out of scope — noted in CHANGELOG.md as follow-ups, not yet filed as GitHub issues.Closes #6166
Test plan
cargo +nightly fmt --checkcargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warningscargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins(13246 passed / 35 skipped / 0 failed)RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler"A2aServerConfig.auth_token(Group C value preserved), deserialize-tolerance test confirming a config omitting a now-skipped field still loadsskip_serializingis unconditional (also closes the runtime-hydrated-Somecase, not justNone), verified no config-diff/hash path depends on Serialize round-tripping these fields