Skip to content

security: validate IPC file paths, sanitize viewer HTML, add CSP (port upstream #27) - #43

Merged
JeanBaptisteRenard merged 3 commits into
mainfrom
security/port-upstream-27-ipc-xss
Jun 9, 2026
Merged

JeanBaptisteRenard merged 3 commits into
mainfrom
security/port-upstream-27-ipc-xss

Conversation

@JeanBaptisteRenard

Copy link
Copy Markdown
Collaborator

Port of upstream doctly/switchboard#27 by @navedr, adapted to the fork's diverged main.js and viewer panels.

Vectors fixed

  • IPC path traversal / secret exfil: read-file-for-panel, save-file-for-panel, watch-file now reject well-known credential locations (.ssh, .gnupg, .aws/credentials, .env, .netrc, .docker/config.json, .kube/config) via isSensitivePath (ipc-path-validator.js). Denylist (not allowlist) because the file panel intentionally accepts arbitrary project paths from terminal OSC 8 hyperlinks.
  • Memory/plan handlers: read-memory/save-memory confined to ~/.claude or active project paths via isAllowedMemoryPath (replaces the weak !startsWith(CLAUDE_DIR) && !existsSync check that let any existing .md be read).
  • Renderer XSS: viewer-panel.js + viewer-toolbar.js sanitize marked.parse() output with DOMPurify before innerHTML. DOMPurify vendored via index.html (same mechanism as morphdom/xterm).
  • CSP: Content-Security-Policy header on the default session (script-src 'self', style-src 'self' 'unsafe-inline' — inline styles are set via JS).

⚠️ Two caveats requiring verification BEFORE merge

  1. CSP needs a runtime check. jsdom doesn't enforce CSP, so unit tests can't catch a white-screen. Must launch the app (SWITCHBOARD_DATA_DIR=~/.switchboard-dev task dev) and confirm no scripts are blocked under file:// and the terminal/CodeMirror/xterm still render.
  2. isAllowedMemoryPath uses activeSessions (live-PTY sessions). If the Plans/Memory panels read files for a selected-but-not-opened project, those reads would now be rejected. Needs confirmation that the renderer only requests memory/plan paths for projects with an active session — otherwise the allowlist must also include the currently-selected project path.

Tests

test/ipc-path-validator.test.js (node:test) — denylist matches, allowlist accepts ~/.claude + project paths, rejects ../ traversal. Native-module suite must be validated via task check in the primary checkout.

Credit: @navedr / doctly#27.

Port of upstream doctly#27 (author @navedr), adapted to the
fork's diverged main.js / viewer panels.

- IPC file handlers (read-file-for-panel, save-file-for-panel, watch-file)
  reject well-known credential paths via isSensitivePath denylist.
- read-memory / save-memory confined to ~/.claude or active project paths
  via isAllowedMemoryPath allowlist (replaces the weak any-existing-.md check).
- Viewer markdown rendering sanitized with DOMPurify before innerHTML
  (viewer-panel.js, viewer-toolbar.js); dompurify vendored via index.html.
- Content-Security-Policy header set on the default session.

See PR body for two runtime caveats requiring verification before merge.
…active sessions

The get-memories panel surfaces CLAUDE.md/agents.md/.claude/*.md from every
indexed project; confining isAllowedMemoryPath to active-session paths would
return empty content for any project without an open session. Enumerate all
known project roots (same source as get-memories) plus ~/.claude.
@JeanBaptisteRenard
JeanBaptisteRenard merged commit 2e66672 into main Jun 9, 2026
7 checks passed
@JeanBaptisteRenard
JeanBaptisteRenard deleted the security/port-upstream-27-ipc-xss branch June 9, 2026 05:34
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