feat: Claude CLI 2.1.50–2.1.53 audit (#171)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m28s
CI / Lint & Test (push) Has started running
CI / Build Linux (push) Has been cancelled
CI / Build Windows (cross-compile) (push) Has been cancelled

## Summary

This PR covers the full audit of Claude CLI changes from 2.1.50 to 2.1.53, plus a batch of bug fixes, new features, and maintenance work identified during that review.

### New Features
- **Workspace trust gate** — detects hooks, MCP servers, and custom commands in a workspace before connecting; persists trust decisions so users aren't prompted repeatedly
- **Custom background image** — users can set a background image with configurable opacity; character panel and compact mode go transparent when active
- **Draggable tab reordering** — conversation tabs can be reordered via pointer-event drag-and-drop (HTML5 drag is intercepted by Tauri/WebView2, so pointer events are used instead)
- **Org UUID in account info** — exposes the org UUID from Claude auth status

### Bug Fixes
- **Unread dot false positives** — initialise unread counts on mount to prevent all tabs showing the blue dot after toggling the file editor (Closes #164)
- **Watchdog for hung WSL bridge** — detects connections that never receive `system:init` and kills the stale process after 1 minute (Closes #166)
- **Suppress terminal window flash on Windows** — applies `CREATE_NO_WINDOW` to all subprocesses via a `HideWindow` trait extension (Closes #165)
- **HTML escaping in markdown renderer** — escape `<` and `>` in `codespan` and `html` renderer callbacks to prevent raw HTML injection (Closes #169)

### Maintenance
- Verify stream-JSON handles tool results above the 50K threshold correctly (Closes #162)
- Reviewed hook security fixes from CLI 2.1.51 — not applicable to our setup (Closes #163)
- Expose org UUID from `claude auth status` (Closes #160)
- Clean up Svelte and Vite build warnings (`a11y_click_events_have_key_events`, `state_referenced_locally`, `non_reactive_update`, `codeSplitting`, chunk size, CodeMirror dynamic import)
- Update all npm dependencies to latest compatible versions with exact pinning (Closes #81, Closes #82, Closes #83, Closes #84, Closes #85, Closes #86, Closes #87, Closes #90, Closes #91, Closes #93, Closes #94, Closes #95, Closes #96, Closes #97, Closes #98, Closes #99, Closes #101, Closes #141, Closes #142, Closes #143, Closes #145, Closes #146, Closes #147)
- Run `cargo update` to bring Cargo.lock up to date

### Closes

Closes #160
Closes #162
Closes #163
Closes #164
Closes #165
Closes #166
Closes #167
Closes #168
Closes #169
Closes #81
Closes #82
Closes #83
Closes #84
Closes #85
Closes #86
Closes #87
Closes #90
Closes #91
Closes #93
Closes #94
Closes #95
Closes #96
Closes #97
Closes #98
Closes #99
Closes #101
Closes #141
Closes #142
Closes #143
Closes #145
Closes #146
Closes #147

 This PR was created with help from Hikari~ 🌸

Reviewed-on: #171
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #171.
This commit is contained in:
2026-02-25 22:55:47 -08:00
committed by Naomi Carrigan
parent 1bb7eb4d26
commit b745100bd5
33 changed files with 2094 additions and 1163 deletions
+27 -44
View File
@@ -190,10 +190,13 @@
<h3 class="text-sm font-medium text-[var(--text-primary)] mb-3">Add MCP Server</h3>
<div class="space-y-3">
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider block mb-1"
<label
for="mcp-new-name"
class="text-xs text-[var(--text-secondary)] uppercase tracking-wider block mb-1"
>Server Name</label
>
<input
id="mcp-new-name"
type="text"
bind:value={newServerName}
placeholder="my-server"
@@ -201,10 +204,13 @@
/>
</div>
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider block mb-1"
<label
for="mcp-new-transport"
class="text-xs text-[var(--text-secondary)] uppercase tracking-wider block mb-1"
>Transport</label
>
<select
id="mcp-new-transport"
bind:value={newServerTransport}
class="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg text-[var(--text-primary)] text-sm focus:outline-none focus:border-[var(--accent-primary)]"
>
@@ -214,10 +220,14 @@
</select>
</div>
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider block mb-1">
<label
for="mcp-new-url"
class="text-xs text-[var(--text-secondary)] uppercase tracking-wider block mb-1"
>
{newServerTransport === "stdio" ? "Command" : "URL"}
</label>
<input
id="mcp-new-url"
type="text"
bind:value={newServerUrl}
placeholder={newServerTransport === "stdio"
@@ -266,6 +276,7 @@
{:else}
<div class="space-y-2">
{#each servers as server (server.name)}
{@const TransportIcon = getTransportIcon(server.transport)}
<button
onclick={() => loadServerDetails(server.name)}
class="w-full bg-[var(--bg-secondary)]/50 rounded-lg p-3 border border-[var(--border-color)] hover:border-[var(--accent-primary)]/50 transition-all text-left"
@@ -274,10 +285,7 @@
<div class="flex items-start justify-between">
<div class="flex-1">
<h4 class="font-medium text-[var(--text-primary)] flex items-center gap-2">
<svelte:component
this={getTransportIcon(server.transport)}
class="w-4 h-4 {getTransportColor(server.transport)}"
/>
<TransportIcon class="w-4 h-4 {getTransportColor(server.transport)}" />
{server.name}
{#if server.status}
{#if server.status.includes("Connected")}
@@ -323,25 +331,19 @@
<RefreshCw class="w-6 h-6 animate-spin text-[var(--text-secondary)]" />
</div>
{:else}
{@const TransportIcon = getTransportIcon(selectedServer.transport)}
<div class="space-y-4">
<!-- Name -->
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider"
>Name</label
>
<p class="text-xs text-[var(--text-secondary)] uppercase tracking-wider">Name</p>
<p class="text-sm text-[var(--text-primary)] mt-1">{selectedServer.name}</p>
</div>
<!-- Transport -->
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider"
>Transport</label
>
<p class="text-xs text-[var(--text-secondary)] uppercase tracking-wider">Transport</p>
<p class="text-sm text-[var(--text-primary)] mt-1 flex items-center gap-2">
<svelte:component
this={getTransportIcon(selectedServer.transport)}
class="w-4 h-4 {getTransportColor(selectedServer.transport)}"
/>
<TransportIcon class="w-4 h-4 {getTransportColor(selectedServer.transport)}" />
{selectedServer.transport.toUpperCase()}
</p>
</div>
@@ -349,9 +351,7 @@
<!-- URL or Command -->
{#if selectedServer.url}
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider"
>URL</label
>
<p class="text-xs text-[var(--text-secondary)] uppercase tracking-wider">URL</p>
<p
class="text-sm text-[var(--text-primary)] mt-1 break-all font-mono bg-[var(--bg-primary)] p-2 rounded border border-[var(--border-color)]"
>
@@ -362,9 +362,7 @@
{#if selectedServer.command}
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider"
>Command</label
>
<p class="text-xs text-[var(--text-secondary)] uppercase tracking-wider">Command</p>
<p
class="text-sm text-[var(--text-primary)] mt-1 font-mono bg-[var(--bg-primary)] p-2 rounded border border-[var(--border-color)]"
>
@@ -376,9 +374,9 @@
<!-- Environment Variables -->
{#if selectedServer.env}
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider"
>Environment</label
>
<p class="text-xs text-[var(--text-secondary)] uppercase tracking-wider">
Environment
</p>
<pre
class="text-xs text-[var(--text-primary)] mt-1 font-mono bg-[var(--bg-primary)] p-2 rounded border border-[var(--border-color)] overflow-x-auto">{JSON.stringify(
selectedServer.env,
@@ -391,9 +389,9 @@
<!-- Full Server Details -->
{#if serverDetails}
<div>
<label class="text-xs text-[var(--text-secondary)] uppercase tracking-wider"
>Full Details</label
>
<p class="text-xs text-[var(--text-secondary)] uppercase tracking-wider">
Full Details
</p>
<pre
class="text-xs text-[var(--text-primary)] mt-1 font-mono bg-[var(--bg-primary)] p-2 rounded border border-[var(--border-color)] overflow-x-auto whitespace-pre-wrap">{serverDetails}</pre>
</div>
@@ -416,18 +414,3 @@
{/if}
</div>
</div>
<style>
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.animate-spin {
animation: spin 1s linear infinite;
}
</style>