Skip to content

Security settings (red-risk confirmation, risk second opinion) are written only through backend commands #856

Open

Description

@smart-ssh-planner

Goal

The security settings "Always confirm on red risk" and the AI risk second opinion (on/off and provider) are changed only through backend commands that validate the value, not by the frontend writing the settings file directly.

Context

  • Decided by maintainer: the webview is treated as part of the trusted computing base, and security switches are changeable only through backend commands, not by direct writes from the frontend.
  • Today the frontend writes these keys straight into settings.json through tauri-plugin-store:
    • apps/smart-ssh-community/frontend/src/riskSettings.ts:32-37 saveRiskClassifierSettings → riskClassifierEnabled, riskClassifierProviderId
    • riskSettings.ts:57-60 saveRedRiskAlwaysConfirm → redRiskAlwaysConfirm
    • callers: src/components/AiProviderSettings.tsx:114, :138
  • The backend reads them at connect(): crates/app-shell/src/risk_second_opinion.rs:33-60 (red_risk_always_confirm is fail-safe "on" for a missing or non-boolean value, Spec 0092 A1.2/A1.3).
  • Settings that already go through backend commands and settings_store::update (crates/app-shell/src/settings_store.rs:122, issue Write settings.json through one helper that keeps memory and file consistent on failure #265): MCP server (mcp_settings.rs:130,234,283), voice language (voice_input.rs:88), chat retention (chat_retention.rs:41), local server notes/tags (local_server.rs:62,69), speech model (speech_models.rs:76). They are the pattern to follow.
  • scripts/check-tauri-commands.mjs checks that every command is defined, registered and invoked.

Requirements

  • New Tauri commands in crates/app-shell (names may follow the existing naming style):
    • set_risk_second_opinion_settings(enabled: bool, provider_id: Option<String>) – rejects a provider_id that is not a configured AI provider (error code, not a panic); enabled = true without a provider id is rejected.
    • set_red_risk_always_confirm(value: bool).
      Both write through settings_store::update (atomic with rollback on a failed save).
  • Read paths stay as they are (frontend may keep reading through the store; backend reading unchanged, including the fail-safe default).
  • riskSettings.ts save functions call the new commands; the file no longer calls store.set, store.delete or store.save.
  • Error codes for rejected values are added to errorCodes.ts and all locales/*/common.json; the settings screen shows the error as it does for other failed saves.
  • Effect timing unchanged: a change applies from the next connection (Spec 0026, Spec 0092 A1.3).
  • Comments in new code in English.

Acceptance Criteria

  • Rust tests: each command writes the expected key/value; an unknown provider id and enabled without provider are rejected and leave the file unchanged.
  • Rust test: set_red_risk_always_confirm(false) followed by reading through red_risk_always_confirm_from_stored yields false; true yields true; the fail-safe for a missing or non-boolean value is unchanged (existing T16 tests pass).
  • Frontend tests (riskSettings.test.ts, AiProviderSettings tests): saving invokes the new commands with the right arguments; no direct store write happens (the store mock's set/save are not called).
  • Guard test (frontend, text scan of non-test src/**/*.ts(x)): the keys redRiskAlwaysConfirm, riskClassifierEnabled, riskClassifierProviderId never appear in a store.set(/store.delete( call; counter-check with an in-memory sample that contains such a call.
  • scripts/check-tauri-commands.mjs, locale parity and lint pass.
  • Relevant automated tests pass.

Technical Notes

  • Security-critical area (risk settings that control confirmation): read .claude/skills/smart-ssh-coder/references/security-invariants.md (invariants 2, 3) and Spec 0092. Review priority elevated.
  • This issue does not yet remove the webview's write permission on the store; a follow-up issue makes the store read-only for the webview and moves the remaining UI preferences behind a backend command.
  • No data migration: the keys and their JSON types stay the same.

Spec impact: update 0092 (§A1: how the setting is written), update 0026 (§3: how the second-opinion setting is written)

Related Work

Activity

  1. added this to the 0.8.0 milestone on Oct 26, 2026
  2. 19 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    Priority

    P1 - High

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions