-
-
Notifications
You must be signed in to change notification settings - Fork 705
fix(security): lock down security-critical config keys from runtime modification #399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8bc5255
3eaa516
96a9412
783a11a
9def148
d6d643e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -435,10 +435,16 @@ export function createConfigEditorController(callTool: ToolCall, trackConfigUiEv | |||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||
| const setResult = await callTool('set_config_value', { | ||||||||||||||||||||||||||||||||||||||||||
| // Include the UI session token when the hosting app has injected it | ||||||||||||||||||||||||||||||||||||||||||
| // (window.__DC_UI_TOKEN). The server validates this token when | ||||||||||||||||||||||||||||||||||||||||||
| // DESKTOP_COMMANDER_UI_TOKEN is set in its environment. | ||||||||||||||||||||||||||||||||||||||||||
| const uiToken = (typeof window !== 'undefined' && (window as any).__DC_UI_TOKEN) | ||||||||||||||||||||||||||||||||||||||||||
| ? String((window as any).__DC_UI_TOKEN) | ||||||||||||||||||||||||||||||||||||||||||
| : undefined; | ||||||||||||||||||||||||||||||||||||||||||
| const setResult = await callTool('_internal_set_config_value', { | ||||||||||||||||||||||||||||||||||||||||||
| key: selected.key, | ||||||||||||||||||||||||||||||||||||||||||
| value: parsed.value, | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+438
to
446
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The UI session token is currently attached to every Severity Level: Major
|
||||||||||||||||||||||||||||||||||||||||||
| // Include the UI session token when the hosting app has injected it | |
| // (window.__DC_UI_TOKEN). The server validates this token when | |
| // DESKTOP_COMMANDER_UI_TOKEN is set in its environment. | |
| const uiToken = (typeof window !== 'undefined' && (window as any).__DC_UI_TOKEN) | |
| ? String((window as any).__DC_UI_TOKEN) | |
| : undefined; | |
| const setResult = await callTool('_internal_set_config_value', { | |
| key: selected.key, | |
| value: parsed.value, | |
| // Include the UI session token only for security-critical keys. | |
| // This reduces credential exposure in tool argument logging/history. | |
| const rawUiToken = typeof window !== 'undefined' ? (window as any).__DC_UI_TOKEN : undefined; | |
| const uiToken = typeof rawUiToken === 'string' ? rawUiToken : undefined; | |
| const requiresUiToken = selected.key === 'blockedCommands' | |
| || selected.key === 'allowedDirectories' | |
| || selected.key === 'defaultShell'; | |
| const setResult = await callTool('_internal_set_config_value', { | |
| key: selected.key, | |
| value: parsed.value, | |
| ...(requiresUiToken && uiToken !== undefined ? { _uiToken: uiToken } : {}), |
Steps of Reproduction ✅
1. Start the Desktop Commander MCP server normally; `src/server.ts` always logs tool calls
via `trackToolCall` (lines 1188–1235) and `toolHistory.addCall` (lines 1468–1507),
regardless of tool name.
2. Host the Config Editor UI so that `bootstrapConfigEditorApp()` in
`src/ui/config-editor/src/app.ts` is executed; it wires `createConfigEditorController` to
a `ToolBridge` that calls backend tools via `bridge.callTool(name, args)` (same file,
bottom third).
3. Ensure the hosting app injects a UI session token into the webview as
`window.__DC_UI_TOKEN` (per the contract documented in `src/server.ts` lines 19–24 and
referenced in `app.ts` lines 438–443); with this set, any config change in the UI (e.g.,
toggling `telemetryEnabled` via the event handlers around lines 540–640) calls
`controller.apply()`, which executes the snippet at
`src/ui/config-editor/src/app.ts:438–447` and sends `_internal_set_config_value` with
arguments `{ key, value, _uiToken }`.
4. On the server, the call enters `call_tool` handling in `src/server.ts`:
`trackToolCall(name, args)` at line ~1222 writes a log line to `TOOL_CALL_FILE`
(`claude_tool_call.log`, defined in `src/config.ts:8–11`) including `Arguments:
${JSON.stringify(args)}`, and `toolHistory.addCall(name, args, result, duration)` at
`src/server.ts:1468–1507` appends a JSON record with the full `args` object to
`tool-history.jsonl` (`src/utils/toolHistory.ts:6–11, 154–171). In both logs, every
`_internal_set_config_value` entry contains the `_uiToken` secret, even when modifying
non-security-critical keys like `telemetryEnabled`, demonstrating that the UI session
token is persistently stored in tool-call history for all config edits, not just
security-critical ones.
5. Note: server-side validation in `src/server.ts:1230–1261` currently requires `_uiToken`
for *all* `_internal_set_config_value` calls when `DESKTOP_COMMANDER_UI_TOKEN` is set; any
change to only send `_uiToken` for some keys must be paired with corresponding server
changes, otherwise non-security-critical config updates from the UI will start failing
with `Error: Unauthorized`.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/ui/config-editor/src/app.ts
**Line:** 438:446
**Comment:**
*Security: The UI session token is currently attached to every `_internal_set_config_value` call, including non-security-critical updates. Because server-side tool history/logging records raw tool arguments, this unnecessarily increases secret exposure. Only include `_uiToken` when changing security-critical keys that actually require elevated UI authorization.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Uh oh!
There was an error while loading. Please reload this page.