Repository navigation
Labels
app-shellfrontendFrontend (React/TypeScript)Frontend (React/TypeScript)manual-testingMerged; some criteria need a manual check on real hardware or another OSMerged; some criteria need a manual check on real hardware or another OSsecurity
Milestone
Description
Goal
The webview can read the settings store but cannot write it. Every settings change goes through a backend command: security settings through their own commands, plain UI preferences through one generic command that only accepts registered preference keys.
Context
- Decided by maintainer: security switches are changeable only through backend commands (Security settings (red-risk confirmation, risk second opinion) are written only through backend commands #856 adds them for the risk settings). As long as the capability grants
store:default, the webview can still write any key ofsettings.jsondirectly, including the security keys; this issue closes that. - Capabilities:
apps/smart-ssh-community/capabilities/default.jsonandmobile.jsongrant"store:default"(includes set/delete/clear/reset/save). - Remaining direct frontend writes to the store (
settingsStore()fromsrc/i18n.ts:36):src/i18n.ts:140language(read by the backend:crates/app-shell/src/ui_language.rs:19)src/firstRunNotice.ts:21first_run_notice_acknowledged(backend:first_run_notice.rs:11)src/voiceInputSettings.ts:23voiceInputEnabledsrc/fileTypeSettings.ts:54fileTypeDefaultAppssrc/layoutSettings.tsfileManagerColumnWidths,aiSshSplitWidthPx,fileManagerHiddenColumns, … (frontend-only keys)src/autoReconnectSettings.ts:30-31autoReconnectSeconds(backend:app_logic::auto_reconnect::SETTING_KEY)
Re-check withgrep -rn "store\.\(set\|delete\|clear\|reset\|save\)(" src --include=*.ts --include=*.tsx(non-test files).
settings_store::update(crates/app-shell/src/settings_store.rs:122) is the one write helper (issue Write settings.json through one helper that keeps memory and file consistent on failure #265).- An embedding app crate can add its own frontend modules that keep UI preferences in the same store; it configures the core through
Wiring(crates/app-shell/src/wiring.rs:30).
Requirements
- New command
set_ui_preference(key: String, value: serde_json::Value)(anddelete_ui_preference(key)if a current caller deletes) incrates/app-shell, writing throughsettings_store::update. It accepts only keys from a UI preference registry; each entry declares the allowed JSON shape (bool, integer range, string with max length, or JSON with max serialised size). Unknown keys and shape violations are rejected with an error code. - The registry contains the keys listed above (and any other current non-security frontend key found by the grep). It never contains a security key (
redRiskAlwaysConfirm,riskClassifierEnabled,riskClassifierProviderId,mcpServer*,localServerTags). - Extension point:
Wiringgets a field to register additional UI preference keys (with their shape) at startup, default empty, so an embedding app can keep its own preferences in the store. Registering a security key or a key already registered fails at startup. - All frontend writers above use
set_ui_preference(a small helper next tosettingsStore()); reads stay on the store. - Both capability files replace
store:defaultwith read-only store permissions (e.g.store:allow-load,store:allow-get-store,store:allow-get,store:allow-has,store:allow-keys,store:allow-values,store:allow-entries,store:allow-length,store:allow-reload; check the plugin's permission list for the exact set). Verify that the frontend still sees values written by the backend (same store instance by path). THREAT-MODEL.md: in the trusted-base subsection and the §6 residual-risk bullet added by Threat model: state that the app's webview is part of the trusted base #818 (add them if Threat model: state that the app's webview is part of the trusted base #818 is not merged yet), state that the webview can read but not write the settings store and that security settings change only through backend commands; the "planned hardening" wording becomes a statement of the current state.- Comments in new code in English.
Acceptance Criteria
- Capability test (Rust or script, run in CI):
default.jsonandmobile.jsoncontain no store permission that can write (store:default,store:allow-set,store:allow-delete,store:allow-clear,store:allow-reset,store:allow-save); counter-check with a sample containingstore:allow-set. - Rust tests:
set_ui_preferenceaccepts each registered key with a valid value; rejects an unknown key, every security key, and a value of the wrong shape/size, leaving the file unchanged. - Rust test: registering an extra key through
Wiringmakes it writable; registering a security key or a duplicate fails at startup. - Frontend: no non-test file calls
store.set,store.delete,store.clear,store.resetorstore.save(guard test with counter-check); the existing tests of the migrated settings modules pass with the store write mocked as aninvoke. - Real-app or e2e check: changing the UI language and the auto-reconnect value in the settings screen still persists across a restart.
-
scripts/check-tauri-commands.mjs, locale parity and lint pass. - Relevant automated tests pass.
Technical Notes
- Security-critical area (capabilities, settings that control confirmation): read
.claude/skills/smart-ssh-coder/references/security-invariants.mdandTHREAT-MODEL.md. Review priority elevated. - No data migration: keys and JSON types stay the same.
- The
Wiringfield is additive with an empty default; the community wiring registers nothing extra.
Spec impact: update 0024 (§4: where UI settings are written), update 0053 (layout preferences written through the backend)
Related Work
- Threat model: state that the app's webview is part of the trusted base #818 (threat model: webview is part of the trusted base)
- Write settings.json through one helper that keeps memory and file consistent on failure #265
- Blocked by Security settings (red-risk confirmation, risk second opinion) are written only through backend commands #856 (risk settings through backend commands)
Metadata
Metadata
Assignees
Labels
app-shellfrontendFrontend (React/TypeScript)Frontend (React/TypeScript)manual-testingMerged; some criteria need a manual check on real hardware or another OSMerged; some criteria need a manual check on real hardware or another OSsecurity
Type
Fields
Priority
P1 - High
Activity
27 remaining items
feat(app-shell): write UI preferences through one validated backend c…
feat(frontend): write UI preferences via the backend; store read-only…
smart-ssh-coder commentedon Nov 2, 2026
Implementation ready for review. PR #1068. Added a validated set_ui_preference command with a key registry and a Wiring extension point. Moved all frontend store writers onto it and reduced both capability files to read-only store permissions. Updated THREAT-MODEL and specs 0024/0053. CI is green on PR #1068.
smart-ssh-reviewer commentedon Nov 2, 2026
Review passed – moving on to merge. Approved. Capabilities are read-only for the store, set_ui_preference validates registered keys/shapes, Wiring extension point fails at startup for security/duplicate keys, all frontend writers migrated, tests and CI green.
Manual testing after the merge:
feat(app-shell): settings store read-only for the webview; UI prefere…