Skip to content

fix(web): server-side sessionId and filePath validation - #216

Merged
DrumRobot merged 6 commits into
mainfrom
fix/cc-sessions-web-security-p0
Aug 6, 2026
Merged

DrumRobot merged 6 commits into
mainfrom
fix/cc-sessions-web-security-p0

Conversation

@DrumRobot

@DrumRobot DrumRobot commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Add server-side format validation for sessionId in /api/session/resume before it reaches the shell-interpolated resume path
  • Replace shell-interpolated execAsync with argv-safe execFile in /api/open-file
  • Loopback-only bind by default, Host header allow-list, one-time token auth, version-coupling guard for older clients
  • Advisory: GHSA-73r5-9hgq-rj58 (publishing alongside this merge)

Test plan

  • [general] Local build passes (tsc/svelte-check clean)
  • [general] Full vitest suite green, including live-server injection-blocked verification
  • [general] Loopback bind verified via LAN-facing interface connection attempt (must fail)
  • [general] Host allow-list verified via forged Host header (must 403)
  • [general] Token issuance/reuse/replay verified against a running server

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Security Enhancements

    • Web sessions now use localhost-only access and token-based authentication.
    • Authentication tokens are exchanged for secure, one-time session cookies.
    • Invalid hosts, tokens, cookies, and replay attempts are rejected.
    • Opening files now safely handles paths without shell interpretation.
  • Improvements

    • The version endpoint reports whether hardened security is available.
    • Server startup and restart provide connection details for supported tooling.

The web server process is spawned without a HOST env var, so
adapter-node defaults to binding 0.0.0.0 (all interfaces) instead of
localhost. Explicitly setting HOST=127.0.0.1 removes the LAN/external
network exposure of the unauthenticated local API.

Signed-off-by: DrumRobot <drumrobot43@gmail.com>
filePath was interpolated directly into a shell command string via
execAsync, letting a crafted path inject and execute arbitrary shell
commands. Switch to execFile with argv-separated arguments so the
input is never parsed by a shell, and split a multi-token
CLAUDE_SESSIONS_EDITOR into its binary plus leading args before
appending filePath.

Adds regression coverage: normal path, an injection-payload path
(asserts it reaches execFile as a single literal argument), and the
multi-token editor split.

Signed-off-by: DrumRobot <drumrobot43@gmail.com>
Reject any request whose Host header doesn't name this loopback server
(localhost / 127.0.0.0/8 / [::1], case/trailing-dot normalized, IPv6
bracket-aware). GET requests carry no Origin header, so Host is the
only anchor available against DNS rebinding or same-LAN access.

Live-verified: evil.com and a LAN-victim-style Host both return 403;
127.0.0.1:<port> and localhost:<port> pass through to 200.

Signed-off-by: DrumRobot <drumrobot43@gmail.com>
…b server (P1-2)

Layer 3 hardening: hooks.server.ts now requires a valid session cookie
or a one-time CLAUDE_SESSIONS_AUTH_TOKEN exchange for every route
except /api/version (health-check). Fixes the 3 flaws the plan doc's
Q2 review found in the original sketch:
- token wasn't actually single-use -> synchronous check-and-set before
  any await, so concurrent requests with the correct token can't both pass
- cookie value was a guessable constant ("valid") -> random per-process
  secret, timing-safe compared
- token leaked indefinitely via the URL -> stripped via 302 redirect
  immediately after a successful exchange

extension.ts generates the token per spawn (env, never argv) and
appends it to every URL it opens (openSession, openWebUI); auth is a
no-op when CLAUDE_SESSIONS_AUTH_TOKEN is unset (standalone `npx
@claude-sessions/web` keeps working without it, matching how HOST is
also extension-only). restartWebServer now returns the handle so
tests/tooling can obtain the token without importing extension
internals.

Verified against a real VS Code extension host (not just curl): the
existing webview.test.ts HTTP-200 checks now perform the token->cookie
exchange before asserting page content, and both they and the
click-through "Session click opens webview" flow pass end-to-end.

Signed-off-by: DrumRobot <drumrobot43@gmail.com>
…rdening (P2-1)

/api/version now reports a hardened: true capability flag — older
@claude-sessions/web builds without hooks.server.ts won't have this
field at all, so its absence is the mismatch signal. Using a
capability flag rather than a semver threshold avoids hardcoding a
"minimum safe version" we can't know yet (this fix hasn't been
released).

extension.ts checks the flag on every health check after it has sent
CLAUDE_SESSIONS_AUTH_TOKEN to the server (a fresh spawn, or an
already-running server from earlier in the same activation) and warns
once per activation if a pinned webServerPath/packageTag points at an
unhardened build — graceful degrade (server keeps working) + surfaced
to the user, per the plan doc's P2-1 guidance.

Live-verified /api/version returns hardened: true; full vscode-extension
integration suite (67 tests) and web suite (125 tests) still pass.

Signed-off-by: DrumRobot <drumrobot43@gmail.com>
…+ Host allow-list (P2-2)

Two of the plan doc's P2-2 checklist items didn't have an automated
equivalent yet (they were only manually curl/lsof-verified during the
P0-1/P1-1 sessions):

- loopback-only bind: connect via this machine's own LAN-facing IPv4
  address (os.networkInterfaces()) instead of localhost — must fail.
  Cross-platform, unlike parsing lsof/netstat output. Skips gracefully
  when no LAN interface exists.
- Host allow-list: a real request against the running server with an
  explicit Host: evil.com header must get 403 (verified Node's
  http.get honors an explicit Host header override before writing this).

The remaining P2-2 items (token-missing 401, token-replay 401,
open-file injection blocked, session-loading regression) were already
covered by hooks.server.test.ts (P1-1/P1-2 commits) and the existing
open-file/server.test.ts — this rounds out the checklist with the two
that specifically need a live server rather than a unit test.

Signed-off-by: DrumRobot <drumrobot43@gmail.com>
@DrumRobot DrumRobot added the bug Something isn't working label Aug 6, 2026
@DrumRobot
DrumRobot marked this pull request as ready for review August 6, 2026 07:20
@DrumRobot
DrumRobot merged commit a523787 into main Aug 6, 2026
3 of 4 checks passed
@DrumRobot
DrumRobot deleted the fix/cc-sessions-web-security-p0 branch August 6, 2026 07:21
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7fae8384-7f26-42a5-8d85-89203de7ea36

📥 Commits

Reviewing files that changed from the base of the PR and between 4feb375 and b2abd76.

📒 Files selected for processing (7)
  • packages/vscode-extension/src/extension.ts
  • packages/vscode-extension/src/test/suite/webview.test.ts
  • packages/web/src/hooks.server.test.ts
  • packages/web/src/hooks.server.ts
  • packages/web/src/routes/api/open-file/+server.ts
  • packages/web/src/routes/api/open-file/server.test.ts
  • packages/web/src/routes/api/version/+server.ts

Cache: Disabled due to Reviews > Disable Cache setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting


📝 Walkthrough

Walkthrough

The web server now uses loopback binding, Host validation, one-time authentication tokens, and session cookies. The VS Code extension passes tokens through server handles and authenticated URLs. The open-file endpoint now uses shell-safe editor execution.

Changes

Web server hardening

Layer / File(s) Summary
Host and session authentication
packages/web/src/hooks.server.ts, packages/web/src/hooks.server.test.ts
The server allows only loopback hosts. Configured tokens create one-time HTTP-only session cookies. Protected requests reject invalid credentials, while /api/version remains public.
Extension server handle integration
packages/vscode-extension/src/extension.ts, packages/web/src/routes/api/version/+server.ts
The extension generates per-process tokens, binds the server to 127.0.0.1, appends tokens to Web UI and session URLs, checks the hardened capability, and returns server handles.
Extension security integration tests
packages/vscode-extension/src/test/suite/webview.test.ts
Integration tests cover loopback-only access, spoofed Host headers, token exchange, and authenticated frontend and session requests.

Safe file opening

Layer / File(s) Summary
Argument-safe editor execution
packages/web/src/routes/api/open-file/+server.ts, packages/web/src/routes/api/open-file/server.test.ts
The endpoint uses execFile with separate editor arguments and file paths. Tests cover shell-injection payloads, multi-token editor commands, and missing inputs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VSCodeExtension
  participant WebServer
  participant hooksServer as hooks.server
  participant Browser
  VSCodeExtension->>WebServer: Start on 127.0.0.1 with auth token
  Browser->>WebServer: Open URL with auth token
  WebServer->>hooksServer: Process request
  hooksServer->>hooksServer: Validate Host and consume token
  hooksServer-->>Browser: Set session cookie and redirect
  Browser->>WebServer: Request protected page with cookie
  WebServer-->>Browser: Return authenticated page
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cc-sessions-web-security-p0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@DrumRobot

Copy link
Copy Markdown
Member Author

🎉 This PR is included in version 0.5.6-beta.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@DrumRobot

Copy link
Copy Markdown
Member Author

🎉 This PR is included in version 0.4.13-beta.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@DrumRobot

Copy link
Copy Markdown
Member Author

🎉 This PR is included in version 0.5.6 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant