Skip to content

Release: security/perf audit fixes + frontend reload state fix#251

Merged
haowei2000 merged 5 commits into
mainfrom
develop
Jul 17, 2026
Merged

Release: security/perf audit fixes + frontend reload state fix#251
haowei2000 merged 5 commits into
mainfrom
develop

Conversation

@haowei2000

Copy link
Copy Markdown
Collaborator

Summary

Test plan

  • Included PRs already merged to develop with their own CI checks
  • Confirm Agent Package Check / CI passes on this promotion PR

haowei2000 and others added 5 commits July 17, 2026 14:21
Deep security + performance audit of frontend, gateway/API, infra, MCP,
and connector. 19 findings fixed, 2 partial, 3 deferred. All fixes were
adversarially re-reviewed; the review's corrections are folded in.

Security
- approval: gate resolve_permission on membership/admin OR bot-owner and
  purge stale approval_delegations + RESPOND grants on member removal (#1)
- agent_bridge: require bot channel membership in handle_acp_event_frame
  before any store write / event-log / broadcast (#3)
- ratelimit: bound eviction (retain live windows + hard-clear fallback) (#12)
- email: never log one-time codes when an email provider is configured (#13)
- CSP: add default/script/style/img/font/connect/media-src; allow https
  images and blob media (#17)
- connector: cap self-update downloads, monotonic anti-downgrade guard,
  bound the loopback pre-auth request body (413) (#14, #24, #15)
- workspace: escape LIKE wildcards; files: normalize content_type to a
  safe allowlist (#22, #23)

Performance
- auth: run all bcrypt hash/verify on spawn_blocking, incl. admin seed (#2)
- db: bounded unread/mention scan (99+), partial index for pending
  permissions, batched Fleet policy resolution (#6, #7, #5)
- gateway: TTL cache for per-trace bot rules, presence roster cache (#8, #9)
- frontend: plain-text streaming render + memoized MarkdownRenderer,
  stable channelFiles memo, offline stream-finalize guard (#10, #11)
- connector: Value::take / mem::take to drop redundant deep clones (#20, #21)

Deferred (documented): #4 Redis-backed cross-replica limiter (interim:
replicaCount=1), #16 httpOnly-cookie session token, #18 Arc fan-out.

Verified offline: server cargo check + 172/172 lib tests; connector cargo
check + 98/98 tests; mcp-server cargo check; frontend typecheck (changed
files clean) + 66/66 vitest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The chat route was `/chat/*` and carried no selection, while
`selectedWorkspaceId` lived only in memory (chatStore persists just
`lastChannelByWorkspace`). So after every reload it was necessarily
null and ChatLayout's bootstrap unconditionally fell back to the
personal workspace — a user reading a team workspace got dumped back
home, and a channel could never be linked or shared.

Put the selection in the path (`/chat/:workspaceId?/:channelId?`) and
make that the source of truth across reloads. The store stays the sole
selection API — all 11 call sites (rail, sidebar, dialogs, invite and
register flows) are untouched — with ChatLayout reconciling the two in
one place: path → store on mount and back/forward, store → path for
in-app selections. `hydrateSelection` applies an explicit pair without
selectWorkspace's last-channel derivation, which would otherwise
clobber the channel the path names.

Two things worth flagging for review:

- The mirror navigates with `replace`, not `push`. On mobile the
  conversation screen is itself a pushed history entry, so an entry per
  channel switch would make Back land on the previous channel instead of
  popping to the list. Refresh-restore and shareable links both work;
  browser back/forward *between channels* is deliberately not wired up,
  as that needs the mobile history model reworked first.

- loadWorkspaces read `selectedWorkspaceId` through its mount-time
  closure, which the path now races: the captured null would overwrite
  the workspace just restored. It reads fresh via getState() and also
  validates membership, so a stale or foreign link falls back home
  instead of resting on a workspace that renders empty.

Verified against the kind stack (vite dev → in-cluster gateway): reload
on a team workspace stays put; reload on a channel restores it; a
non-member workspace id in the path falls back and corrects the URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cargo fmt --check on the agent packages requires the (mem::take, clone)
tuple from the #21 fix to wrap across lines. Formatting only, no behavior
change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Security + performance audit fixes (19 fixed, 2 partial, 3 deferred)
…resh-d169b8

fix(frontend): keep the open workspace/channel across a reload
@haowei2000
haowei2000 merged commit 33405cb into main Jul 17, 2026
11 checks passed
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