Repository navigation
Redacting Debug for input types with secrets; constant-time MCP token check #848
Copy link
Copy link
Open
Description
Goal
Input types that carry plaintext secrets cannot leak them through Debug (for example through a future tracing::debug!(?input)), and the local MCP server compares its bearer token in constant time and keeps it as a secret type.
Context
- Input DTOs with plaintext secrets and
#[derive(Debug)]:crates/app-logic/src/dto.rs:344-357AiProviderConfigInput.api_key: Stringcrates/app-logic/src/dto.rs:694-712ServerInput.sudo_password: Option<String>crates/app-logic/src/dto.rs:786-832AuthMethodInput(value,key_content,passphrase,cert_content)crates/app-shell/src/mcp_settings.rs:36-50McpServerSettingsDto.token(output DTO; the token is shown to the user on purpose, butDebugmust not print it)
Today nothing logs these with?(searched), so this is hardening, not a fix of a current leak.
- Pattern already in the repo:
Preparedincrates/core/src/outgoing.rs:66-73with a hand-writtenDebugprinting[REDACTED], tested atoutgoing.rs:99-107.secrecy0.10 is a dependency of core and app-logic. crates/app-shell/src/commands/ai_providers.rs:203-211copies the API key into a plainString(expose_secret().to_string()) that is never zeroed.- MCP auth:
crates/mcp-server/src/auth.rs:20-75keeps the token asArc<Mutex<String>>(SharedToken) and compares withtoken == expected; the comment says constant time would be extra hardening that the spec does not require. The server binds to127.0.0.1(crates/mcp-server/src/config.rs:34).subtle2.6 is already inCargo.lock. crates/core/src/credentials/mod.rs:1-15module doc still describes "planned content"; the real implementation lives incrates/core/src/profiles/credentials.rsandcrates/credentials-keyring.
Requirements
- Each DTO above gets a
Debugthat prints secret fields as[REDACTED](presence may be shown, e.g.Some([REDACTED])), either by a hand-writtenDebugor by changing the field type toSecretStringwith serde deserialization (secrecy'sserdefeature). PreferSecretStringwhere the field flows on into code that already expects a secret; keep the IPC JSON shape unchanged in both cases. ai_providers.rs:203-211: keep the key asSecretString(orZeroizing<String>) until the point where the HTTP client needs it.SharedTokenstores the token asSecretString, has noDebugthat prints it, andrequire_bearer_tokencompares withsubtle::ConstantTimeEqon the byte slices (a length mismatch may return early). The empty-token rule (issue Reject MCP requests when the expected token is empty #115) stays.- Update the outdated comment in
auth.rsand the module doc ofcrates/core/src/credentials/mod.rs(point to where credentials actually live). - Add a short rule to
AGENTS.md(or the security invariants file) next to the logging rules: "types that carry secrets have a redactingDebug". - Comments in new code in English.
Acceptance Criteria
- Test per DTO (
AiProviderConfigInput,ServerInput, everyAuthMethodInputvariant with a secret,McpServerSettingsDto):format!("{:?}", value)built from a test secret does not contain the test secret. - Counter-check: the same test with a plain
#[derive(Debug)]would fail (state it in the PR, or keep a test that checks the non-secret fields are still printed). - Deserialisation tests for the changed DTOs: the existing IPC JSON (camelCase field names,
nullfor absent values) still deserialises into the same values. - MCP auth tests (
auth.rstests: missing, wrong, correct, rotated, empty token) pass unchanged; a new test checks that a token differing only in the last byte and a token of different length are rejected. -
grep -n "token == expected" crates/mcp-serverreturns nothing. - Relevant automated tests pass.
Technical Notes
- Security-critical area (credentials, MCP authentication): read
.claude/skills/smart-ssh-coder/references/security-invariants.md(section "Redaction und Geheimnisse") and ADR 0086 (content only at debug level). Review priority elevated. - No change to persisted data, IPC payloads or behaviour visible to the user.
tauri::commandargument types must keep deserialising from the same JSON;scripts/check-tauri-commands.mjsmust still pass.
Spec impact: none
Related Work
- Reject MCP requests when the expected token is empty #115 (empty MCP token never grants access)
Metadata
Metadata
Assignees
Type
Fields
Priority
P1 - High
Activity
18 remaining items