test(ai-chat): crypto/SSRF/assistant-parts coverage + a11y + refactors #10
Reference in New Issue
Block a user
Delete Branch "feat/ai-chat-review-followups"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements
docs/backlog/ai-chat-review-followups.md(the non-blocking follow-ups from the ai-chat multi-aspect review).What
Adds test coverage for previously-untested security-critical code, fixes two keyboard-a11y gaps, and lands several small refactors flagged in review.
How
Priority 1 — tests (no prod-code behavior change except exporting pure helpers for testing):
secret-box.spec.ts— AES-256-GCM round-trip; random salt+iv → different blobs both decrypt; tampered ciphertext / flipped auth-tag byte → throws the "APP_SECRET may have changed" message; wrong APP_SECRET → throws.ssrf-guard.spec.ts—isIpAllowedblocks loopback/link-local+metadata/private/CGNAT/ULA/unspecified/IPv4-mapped, allows public;isUrlAllowedblocks bad scheme, invalid URL, IP literals, and DNS-rebinding (mockeddns.lookup).ai-chat.service.spec.ts—assistantPartsemits a syntheticoutput-errorfor an unpaired tool call (the load-bearing guard for theMissingToolResultsErrorfix — fails on pre-fix logic),output-availablewhen paired, skips malformed calls; plusserializeSteps/rowToUiMessage.ai-chat-tools.service.spec.ts— JSON-stringnode/contentcoercion forwards as object, invalid JSON throws the documented message,updatePageJsontitle-only vs object.page-embedding.repo.spec.ts— emptyspaceIdsearly-returns[]with no DB call.Priority 2 — a11y + docs: Chat-history toggle + conversation rows are now keyboard-operable (
role=button/tabIndex/Enter+Space), matching the existinghistory-item.tsxpattern; stale tool-list comment intool-parts.tsxrefreshed.Priority 3 — refactors:
useChat.onErroradopts the server chat id when the first turn errors (AI SDK v6onFinishdoesn't fire on error);isToolPartexported once and shared (was duplicated in two components);buildInitialValues()dedups the MCP-server-form initial values;describeProviderErrorreplaces two inlinestatusCode: messagesnippets so provider-error formatting is unified.Reasoning / decisions
assistantParts/serializeSteps/rowToUiMessagepurely for testability (commented as test-only; no runtime change) — the unpaired-tool-call→output-errorbranch is the regression most worth pinning, since losing it silently reintroduces a next-turn crash.secret-boxat rest, and SSRF-guard is the only defense for admin-set external-MCP URLs — both had zero tests, so these are the highest-value additions.Review findings
docs/ai-agent-chat-plan.md§5.3/§6.3 to correct the crypto description, but that file does not exist in the repo (grep-confirmed).secret-box.tsalready documents the real layout. Flagged rather than inventing a target.src/-rooted module-resolution gap makes ~16 unrelated suites fail to load (untouched files, e.g. PageService graph) — not introduced here.Verification
pnpm --filter server build+pnpm --filter client build— clean.pnpm --filter server test -- secret-box ssrf-guard ai-chat.service ai-chat-tools.service page-embedding.repo— 59/59 pass (5 suites).aria-expandedand shows/hides the list; Enter on a conversation row switches the active chat; assistant messages render with no broken items (isToolPart refactor intact). No app errors. Screenshot captured.🤖 Generated with Claude Code
3a04e09f13toec128d54b4Ghost referenced this pull request2026-06-24 00:42:55 +03:00
Ghost referenced this pull request2026-06-25 12:25:48 +03:00
Ghost referenced this pull request2026-06-28 17:36:05 +03:00