Repository navigation
Redaction scope and server tags: lookup failures never weaken masking or rules and show a notice #843
Copy link
Copy link
Open
Description
Goal
When the app cannot fully determine which secret values to mask or which server tags apply, it never masks less or decides more leniently than before, and the user sees a notice in the session instead of a silent gap.
Context
Redaction scope (extension secrets for server, groups and tags, Spec 0038 §2a):
scope_for_id(crates/app-logic/src/session_extension.rs:84-100):get_servererror →None;known_secrets(ext, None)(:124-132) then returns no extension secrets at all, without a log line. Caller:crates/app-shell/src/commands/connect.rs:1633-1641(extension_scope).server_scope(:60-79):group_chainerror → warn, scope without groups → group secrets are not masked for the session.rebuild_redactors_for_sessions(:190-219):get_servererror →group_chain = Vec::new(), thenswap_redactor_with_literalsreplaces the literal set, so group secrets that were masked before drop out of the redactor.redactor_with_secrets(:106-121):Regex::new(®ex::escape(s)).ok()silently drops a literal that fails to compile (in practice only the regex size limit for very long values).read_sudo_password_for_redaction(crates/app-logic/src/server_redaction.rs:65-83): backend error → warn only, the session does not mask the stored sudo password.
Server tags for rule evaluation:tags_for_session(crates/app-logic/src/orchestration/action_exec.rs:891-897) and the re-evaluation of an edited command inhandle_user_decision(:1623-1627): anyget_servererror → the tags frozen at connect time are used. Rule evaluation then uses possibly outdated tags without any signal.- The local pseudo-server has no stored row (
connect.rs:1630-1632), soServerNotFoundis a normal case there; security invariant 17 forbids special-casing it in security logic, but distinguishing "not stored" (ProfileError::ServerNotFound) from a store failure is not a special case. - Pattern for a one-time session notice:
announce_second_opinion_setup_failure(action_exec.rs:2436-2470, pending flag onSession,emit_chat_errorwith a code).
Requirements
- Scope lookups distinguish "server not stored" from "store failed". On a store failure (
get_serverorgroup_chain):scope_for_idreturns a scope with at leastserver_id(empty groups/tags) instead ofNone, so server-level extension secrets stay masked;- the session is marked "redaction incomplete".
rebuild_redactors_for_sessions: if the scope cannot be fully determined, it extends the existing literal set of each affected session with the newly known secrets instead of replacing it, logs a warning (server id, error kind, no content) and marks the sessions "redaction incomplete".redactor_with_secrets: a literal that cannot be compiled is logged (count and byte length only, never the value) and marks the session "redaction incomplete". Recommended: retry with a largerRegexBuilder::size_limitbefore giving up.read_sudo_password_for_redactionbackend error: also marks the session "redaction incomplete" (log line unchanged).- "Redaction incomplete" shows one chat notice per session (pending flag, like
announce_second_opinion_setup_failure), codeREDACTION_INCOMPLETE, text in the backend's English style, e.g. "Some secret values for this server could not be loaded; they may not be masked in what is sent to the AI." Add the code toapps/smart-ssh-community/frontend/src/errorCodes.tsand alllocales/*/common.json(notice codes such asAI_SECOND_OPINION_SETUP_FAILEDare mapped there). - Server tags: on a store failure other than
ServerNotFound,handle_action_proposedstill evaluates with the frozen tags, but anAutoExecresult escalates toConfirmwith codeFILTER_SERVER_TAGS_UNAVAILABLE(a new fact/step inescalate(), Escalation chain: one pure escalate() function shared by the decision path and the rule-suggestion verdict #826). Inhandle_user_decisiona store failure logs a warning; the re-evaluation keeps the frozen tags (onlyDenymatters there).ServerNotFoundkeeps today's behaviour. Add the code toerrorCodes.tsand all locales. - Comments in new code in English.
Acceptance Criteria
- Test:
get_serverfails with a backend error at session start → a server-level extension secret is still masked in the session's redactor, and the session emits exactly oneREDACTION_INCOMPLETEnotice (on the first chat turn or action). - Test:
group_chainfails →REDACTION_INCOMPLETEnotice; server-level secrets masked. - Test: rebuild with
get_serverfailing → a group secret that was masked before the rebuild is still masked after it (counter-check: with a healthy store the rebuild still replaces the set, existing testtest_rebuild_replaces_literals_but_reconnect_swap_keeps_thempasses). - Test: a secret literal longer than the default regex size limit is either masked (after the retry) or produces the notice; no value appears in the log output (captured with a tracing test subscriber).
- Test: profile store backend error during
tags_for_sessionwith a matching allow rule →ConfirmwithFILTER_SERVER_TAGS_UNAVAILABLE, neverAutoExec; withServerNotFound→ unchanged decision (counter-check). - Locale parity and error-code checks pass.
- Relevant automated tests pass.
Technical Notes
- Security-critical area (redaction, filter inputs, escalation chain): read
.claude/skills/smart-ssh-coder/references/security-invariants.md(invariants 2, 11, 17) andTHREAT-MODEL.md§2. Review priority elevated. - The
SessionExtensiontrait signature (known_secrets(&self, scope) -> Vec<SecretString>) stays unchanged; extensions are not affected. - Builds on the pure
escalate()function (Escalation chain: one pure escalate() function shared by the decision path and the rule-suggestion verdict #826) for the new tag step; add the step after the existing ones so codes of earlier steps win. - Logging follows ADR 0086: ids and error kinds, never contents.
Spec impact: update 0038 (§2a: behaviour when the scope cannot be determined)
Related Work
- Blocked by Escalation chain: one pure escalate() function shared by the decision path and the rule-suggestion verdict #826
- Extension point: session context section and per-server known secrets for redaction #463, Redaction: let an extension rebuild replace the literal secret values of open sessions #635 (extension secrets for redaction and their rebuild), Show when the AI second opinion or injection check could not run instead of skipping it silently #102 (second-opinion setup notice)
Metadata
Metadata
Assignees
Type
Fields
Priority
P1 - High
Activity
18 remaining items