Skip to content

Commit 631009d

Browse files
authored
Merge pull request #209 from oratis/codex/minimize-desktop-capabilities
security: minimize desktop renderer capabilities
2 parents a4b4556 + 5ac7444 commit 631009d

12 files changed

Lines changed: 65 additions & 31 deletions

File tree

apps/desktop/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
"@deepcode/shared-ui": "workspace:*",
2828
"@tauri-apps/api": "^2.0.0",
2929
"@tauri-apps/plugin-dialog": "^2.0.0",
30-
"@tauri-apps/plugin-fs": "^2.0.0",
3130
"@tauri-apps/plugin-opener": "^2.0.0",
3231
"@tauri-apps/plugin-process": "^2.3.1",
3332
"@tauri-apps/plugin-shell": "^2.0.0",

apps/desktop/src-tauri/Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src-tauri/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ tauri-build = { version = "2", features = [] }
1616
[dependencies]
1717
tauri = { version = "2", features = [] }
1818
tauri-plugin-dialog = "2"
19-
tauri-plugin-fs = "2"
2019
tauri-plugin-opener = "2"
2120
tauri-plugin-shell = "2"
2221
tauri-plugin-updater = "2"

apps/desktop/src-tauri/capabilities/default.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
"core:window:allow-minimize",
1111
"core:window:allow-maximize",
1212
"core:window:allow-close",
13-
"dialog:default",
14-
"fs:default",
15-
"opener:default",
13+
"dialog:allow-open",
14+
"opener:allow-default-urls",
1615
"updater:default",
17-
"process:default",
1816
"process:allow-restart"
1917
]
2018
}

apps/desktop/src-tauri/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ pub fn run() {
3434
tauri::Builder::default()
3535
.plugin(tauri_plugin_opener::init())
3636
.plugin(tauri_plugin_dialog::init())
37-
.plugin(tauri_plugin_fs::init())
3837
.plugin(tauri_plugin_shell::init())
3938
.plugin(tauri_plugin_updater::Builder::new().build())
4039
.plugin(tauri_plugin_process::init())

apps/desktop/src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
}
2626
],
2727
"security": {
28-
"csp": "default-src 'self' tauri:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: tauri: http://asset.localhost https://asset.localhost; font-src 'self' data:; connect-src 'self' tauri: ipc: ws://localhost:5173 https://api.deepseek.com"
28+
"csp": "default-src 'self' tauri:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: tauri: http://asset.localhost https://asset.localhost; font-src 'self' data:; connect-src 'self' tauri: ipc: ws://localhost:5173"
2929
}
3030
},
3131
"bundle": {

docs/CODEX_ALIGNMENT_PLAN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ model tool call
350350
- 已删除无生产引用的 core renderer IPC、desktop native mutation/tool facade 与 `chat` 路由别名;
351351
Tauri 仅保留 credential presence、文件/快照只读投影和 sidecar supervision。legacy session importer
352352
继续只读,旧文件不迁移、不覆盖;用户文档已改为 app-server 架构。
353+
- Tauri capability 已收紧为 open dialog、默认 URL、updater 与 restart 的精确 allow 集;未使用的 FS
354+
plugin、generic process exit、file reveal 和 renderer → provider CSP 路径已移除,release gate 防止回归。
353355
- `pnpm release:check` 已对真实 VS Code/app-server/VSIX 产物执行包体预算、v1 capability、
354356
create/read、配置诊断、workspace diff、重启 read/resume 与薄客户端边界扫描;Ubuntu/macOS CI
355357
与 tag validate 共用该门禁,tag 还必须通过 desktop Playwright journey。迁移、隔离 home 回滚演练、

docs/design/release-gates-v1.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ The gate fails unless all of the following hold:
2828
and resume the same canonical thread.
2929
5. Desktop, VS Code, and LSP production sources do not import the provider, credentials, agent loop,
3030
or `RuntimeHost`, and do not contain renderer credential escape hatches.
31+
6. The Tauri renderer has no filesystem default capability, process-exit capability, file-reveal
32+
capability, or provider API CSP access; required dialog, URL-open, and restart features use
33+
narrow allow permissions.
3134

3235
The current budgets deliberately leave cross-platform CI headroom over the measured baseline. A
3336
budget increase requires a PR description with before/after measurements and an explanation of the

docs/security-model.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# DeepCode Security Model
22

3-
> Last updated: 2026-08-01 (shared app-server trust and config provenance)
3+
> Last updated: 2026-08-01 (shared app-server trust, provenance, and thin-client capabilities)
44
55
This document is the **single source of truth** for what DeepCode protects
66
against, what it doesn't, and how each layer composes. If you're reviewing a
@@ -17,13 +17,23 @@ decreasing order of operator severity:
1717
| 1 | Model exfiltrates DeepSeek API key (or other env secrets) via tool call | High | M3.5 sandbox + M5.1 env strip |
1818
| 2 | Model writes arbitrary files outside the project (`/usr/bin`, `/etc`) | High | M3.5 sandbox + permissions |
1919
| 3 | Plugin (third-party code) does either #1 or #2 | High | M5.1 subprocess + (M5.1-ext) OS sandbox |
20-
| 4 | Hook script (third-party shell snippet) does either #1 or #2 | Medium | M3.5 sandbox wraps Bash; hooks bypass when invoked via /bin/sh directly |
20+
| 4 | Hook script (third-party shell snippet) does either #1 or #2 | Medium | Exact-definition review + source trust; hook commands remain host code |
2121
| 5 | Hostile `settings.json` field (e.g. allowRead path) injects sandbox rule | Medium | escapeSbpl() |
2222
| 6 | Untrusted project's AGENTS.md drives the agent into harmful action | Low | Trust store (`/trust`) |
2323
| 7 | DNS exfiltration of secrets from sandboxed Bash | Partly mitigated | M3.5-ext DNS allowlist (netns.ts) — names only; raw-IP dials still pass |
2424

2525
## Defence layers
2626

27+
### Runtime placement — thin clients
28+
29+
The Tauri WebView, VS Code extension, and LSP process are protocol clients, not trusted runtimes.
30+
Provider credentials, model calls, tools, permissions, hooks, MCP, and plugins stay in the CLI host
31+
or app-server. The Tauri capability set is least-privilege: file/folder selection, default
32+
HTTP(S)/mailto/tel URL opening, updater operations, and application restart. It has no Tauri
33+
filesystem plugin, shell permission, generic process-exit permission, file-reveal permission, or CSP
34+
route to the provider API. Read-only file preview and session history use explicit Rust commands
35+
that reject the backend credential path; all workspace mutations use the versioned protocol.
36+
2737
### Layer 0 — Trust store
2838

2939
First time DeepCode opens a folder, you're asked **"Do you trust this
@@ -124,15 +134,16 @@ not an oversight. (M5.2 will add per-clause analysis.)
124134

125135
Plugins run in their own `node` subprocess with:
126136

127-
- **No host fs/net access** in plugin code — all capabilities (`fs_read`,
128-
`fs_write`, `bash`, `fetch`) flow via JSON-RPC over stdio back to the host,
129-
which applies its own mode/permission/sandbox stack.
137+
- **Gated host capabilities** — declared `fs_read`, `fs_write`, `bash`, and `fetch` requests flow via
138+
JSON-RPC over stdio back to the host, which applies mode, permission, hook, approval, and sandbox
139+
policy. This controls the supported plugin API; it is not yet an OS boundary against a malicious
140+
plugin using Node APIs directly.
130141
- **Token-protected RPC** — host generates an unguessable token per plugin
131142
spawn; every RPC from the plugin must include it.
132143
- **Env scrub**`DEEPSEEK_API_KEY` and `DEEPSEEK_AUTH_TOKEN` are stripped
133144
from the child env. Plugins cannot read DeepSeek credentials.
134-
- **Hash pin** — plugin code is SHA-256 hashed at install time; mismatch on
135-
load fails open (drift detection).
145+
- **Whole-install hash pin**every installed plugin file contributes to the SHA-256 trust hash;
146+
missing trust or any drift disables the plugin and reports a diagnostic.
136147

137148
**Acknowledged gaps**:
138149

@@ -166,25 +177,25 @@ etc.) as **untrusted**. We:
166177

167178
## Attack-vector test suite
168179

169-
`packages/core/src/sandbox/attacks.test.ts` contains 17 tests:
180+
`packages/core/src/sandbox/attacks.test.ts` and the platform integration suites cover:
170181

171-
- **6 unit-level** "hostile input → safe output" tests:
182+
- **Unit-level** "hostile input → safe output" cases:
172183
- SBPL paren/quote escaping
173184
- SBPL backslash escaping
174185
- deny-after-allow ordering
175186
- no implicit network when allowedDomains is empty
176187
- no implicit file-write to /usr, /System, /Library
177-
- **3 bwrap-arg safety** tests:
188+
- **bwrap-argument safety** cases:
178189
- no --share-net even with a non-empty allowedDomains (connectivity comes from
179190
slirp4netns externally, never by sharing the host netns)
180191
- only cwd is bare --bind
181192
- always --unshare-{pid,ipc,uts}
182-
- **4 excluded-command spoofing** tests:
193+
- **Excluded-command spoofing** cases:
183194
- prefix-only match (`gitleaks`) does NOT bypass
184195
- exact match bypasses
185196
- leading-token match bypasses
186197
- pipeline-after-excluded bypasses (documented behavior; M5.2 hardens)
187-
- **2 sandbox-exec e2e** (macOS, runIf the binary exists):
198+
- **sandbox-exec e2e** (macOS, runIf the binary exists):
188199
- block write to `/usr/local/bin/*`
189200
- profile is syntactically valid (smoke)
190201
- **bwrap e2e** (Linux CI, runIf `bwrap` exists — `bwrap-integration.test.ts`):

pnpm-lock.yaml

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)