Skip to content

Security, perf, and robustness hardening (audit-driven) + README refresh#27

Merged
aletc1 merged 2 commits intodevfrom
claude/heuristic-sammet-0a6a36
Apr 18, 2026
Merged

Security, perf, and robustness hardening (audit-driven) + README refresh#27
aletc1 merged 2 commits intodevfrom
claude/heuristic-sammet-0a6a36

Conversation

@aletc1
Copy link
Copy Markdown
Owner

@aletc1 aletc1 commented Apr 18, 2026

Summary

Two commits shipped off a security/correctness/performance audit of src/main/**. Every finding was verified by direct source reads — a few initial agent claims were dismissed (see commit body).

Security (P0)

  • Shell-injection hardeningvoice.ts and claude/env.ts no longer interpolate $SHELL into a shell string; they now use execFileSync(shell, [...argv]) with a safe-shell allowlist. claude-token.ts drops shell: true and resolves the claude CLI to an absolute path via which/where before spawning.
  • OAuth deep-link CSRF fix — The /auth handler now requires an in-flight flow initiated by startAuthFlow(). A per-flow state nonce is generated, attached to the auth URL, and verified with constant-time compare on callback. Drive-by deep links (twentyfirst-agents://auth?code=... from a malicious page) are rejected.

Performance (P1)

  • PRAGMA synchronous = NORMAL under WAL — cuts fsync load during streaming persistence.
  • FK indexes on chats.project_id, sub_chats.chat_id, and sub_chats.stream_id (previously full-table scans). New migration drizzle/0009_wise_rumiko_fujikawa.sql.
  • text-delta coalescing — 24 ms buffer in the Claude streaming subscription, keyed by text id. Non-delta chunks flush first so ordering is preserved. Reduces tRPC/IPC churn and renderer re-render pressure on long responses.

Robustness (P2)

  • Migration failure recovery — the DB file is quarantined to agents.db.broken-<timestamp> and a fresh DB initialized, so corrupt-DB state can't brick app launch anymore.
  • Bounded cleanupGitWatchers() on quit (1.5 s) so a hung chokidar instance can't block shutdown.

Docs

Out of scope / dismissed during audit

  • sandbox: false in webPreferences is intentional for electron-trpc (contextIsolation remains on) — defense-in-depth, not a critical gap.
  • activeSessions cleanup is already wired in both the stream's finally and the observable's unsubscribe handler.
  • The long async IIFE in claude.ts is wrapped in try/catch/finally; no unhandled-rejection path.
  • Project deletion is atomic under SQLite FK cascade with foreign_keys=ON.

Test plan

  • Set SHELL='/bin/zsh; echo pwned > /tmp/pwn', trigger the voice and shell-env bootstrap paths, confirm /tmp/pwn is not written.
  • Run open "twentyfirst-agents-dev://auth?code=foo" without clicking Sign In first → rejected in log, no exchange attempted.
  • Click Sign In, complete OAuth → login still works end-to-end.
  • After migration lands, EXPLAIN QUERY PLAN SELECT * FROM sub_chats WHERE chat_id=? → uses sub_chats_chat_id_idx.
  • Stream a long (>2 k token) response, profile the renderer main thread — emit count drops ~10× with identical final output.
  • Truncate agents.db to simulate corruption → relaunch produces a fresh DB with agents.db.broken-* sibling.
  • Open a project, quit the app, relaunch → no stale file handles on the project dir.
  • Smoke: bun run dev, create chat, send message in Plan and Agent mode, rollback, fork, switch sub-chats.

aletc1 and others added 2 commits April 18, 2026 19:04
Addresses critical findings from a security/correctness/performance audit.

Security (P0):
- voice.ts, claude/env.ts: replace `execSync(${shell} -ilc '...')` with
  `execFileSync(shell, ['-ilc', ...])` and add a safe-shell allowlist to
  block `$SHELL=` command-injection via template interpolation.
- claude-token.ts: remove `shell: true` from the `claude setup-token`
  spawn and resolve the CLI to an absolute path via `which`/`where`
  before invoking, so metacharacters in PATH can't be interpreted.
- auth-manager.ts + index.ts: the `/auth` deep-link handler now requires
  an in-flight OAuth flow initiated by `startAuthFlow()`. A per-flow
  `state` nonce is generated, attached to the auth URL, and verified
  with constant-time compare on callback. Unsolicited deep links (drive-
  by CSRF via `twentyfirst-agents://auth?code=...`) are rejected.

Performance (P1):
- db/index.ts: add `PRAGMA synchronous = NORMAL` under WAL to reduce
  fsync load during high-frequency message persistence.
- db/schema/index.ts + drizzle/0009_*: add indexes on `chats.project_id`,
  `sub_chats.chat_id`, and `sub_chats.stream_id` (previously full-table
  scans on hot FK lookups).
- claude.ts: coalesce `text-delta` chunks in a 24ms buffer keyed by
  text id. Non-delta chunks flush the buffer first to preserve ordering.
  Cuts tRPC/IPC chatter during long streaming responses.

Robustness (P2):
- db/index.ts: migration failures now quarantine the DB file to
  `agents.db.broken-<timestamp>` and start fresh instead of crashing
  the app on every launch.
- index.ts: bound `cleanupGitWatchers()` in `before-quit` to 1.5s so a
  hung chokidar instance can't block app quit indefinitely.

Dismissed during verification:
- `sandbox: false` in webPreferences is intentional for electron-trpc;
  contextIsolation remains enabled.
- `activeSessions` map cleanup is already wired on unsubscribe and in
  the stream's finally block.
- The stream's async IIFE is wrapped in try/catch/finally; no
  unhandled rejection.
- Project deletion is atomic under FK cascade with `foreign_keys=ON`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the user-visible features shipped since the README was last touched
(commits #18 through #26) and groups the fork additions into four
sections for readability: Workflow & UI, Git/PRs/Worktrees, Models, and
Stability & Polish.

Highlights surfaced: split view with drag-to-split, Cmd+Shift+T, sortable
sidebar, draggable tabs, queue reorder, copy popover, PR widget +
branch switcher, two-column commit diff, Pull & Push recovery dialog,
worktree deletion safety, Sonnet 4.6 1M context + recovery action,
GPT-5.4, rich tool rendering, stream wedge timeout, and crash
auto-recovery.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@aletc1 aletc1 merged commit 3f0f7a8 into dev Apr 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant