Skip to content

security: harden API, prevent injection, and restrict access#1

Open
xtalax wants to merge 2 commits intoandrew-yangy:mainfrom
xtalax:security/harden-api-and-inputs
Open

security: harden API, prevent injection, and restrict access#1
xtalax wants to merge 2 commits intoandrew-yangy:mainfrom
xtalax:security/harden-api-and-inputs

Conversation

@xtalax
Copy link
Copy Markdown

@xtalax xtalax commented Feb 28, 2026

Summary

Security hardening across the server codebase, addressing critical vulnerabilities found during audit:

  • CORS restriction — replaced wildcard * with explicit localhost origin allowlist, preventing any webpage from calling the API
  • Request body size limits — added a readBody() helper with configurable max size (1MB default, 8KB for input endpoints) to prevent memory exhaustion DoS
  • Path traversal fixserveStatic() now validates resolved path stays within dist directory via path.resolve containment
  • AppleScript injection prevention — TTY paths validated against /^\/dev\/ttys\d+$/ before interpolation into AppleScript strings
  • Improved AppleScript escaping — now handles \n, \r, \t in addition to \ and "
  • Team name validation hardened — strict [a-zA-Z0-9_-] allowlist plus defense-in-depth resolved-path containment check
  • Event receiver input validation — field type checks, length truncation, ISO 8601 timestamp validation, metadata size cap
  • Config PATCH hardened — rejects unexpected keys beyond macOS/browser
  • File permissions restricted~/.conductor/ dir 0o700, config/db files 0o600
  • Security headers addedX-Content-Type-Options: nosniff, X-Frame-Options: DENY, Content-Security-Policy

Motivation

The highest-impact finding: CORS * + zero authentication means any website visited while the server is running can send keystrokes to terminal panes via POST /api/actions/send-input. The CORS fix alone closes that attack surface from cross-origin contexts.

Test coverage

75 new tests across 3 test files (136 total with existing 61):

server/security.test.ts (28 tests)

  • CORS origin allowlist — allowed origins, blocked origins, missing origin header, HTTPS variant rejected
  • Security headers — CSP, X-Frame-Options, X-Content-Type-Options
  • readBody size limits — normal read, chunked, empty body, exact limit, overflow rejection
  • serveStatic path traversal — ../ blocked, internal .. blocked, SPA fallback, correct MIME types

server/actions/cleanup.test.ts (18 tests)

  • Valid team deletion with hyphens, underscores, digits
  • Path traversal: ../, /, bare .., dot-prefixed names
  • Special char rejection: spaces, semicolons, backticks, $, null bytes, newlines, backslashes, unicode

server/hooks/event-receiver.test.ts (29 tests)

  • Normal input passthrough, metadata preservation
  • Missing field defaults (type, sessionId, timestamp, message, project)
  • Field truncation (type@128, sessionId@256, message@1024, project@256)
  • Timestamp validation (valid ISO, invalid strings, non-string types)
  • Metadata size limit (small preserved, >8KB dropped)
  • Type coercion (non-string inputs default correctly)

Refactoring for testability

  • Extracted security utilities into server/security.ts (CORS, headers, readBody, serveStatic)
  • Extracted sanitizeEventBody from event-receiver.ts for DB-free unit testing
  • Added npm test script to package.json
$ npm test
ℹ tests 136
ℹ suites 23
ℹ pass 136
ℹ fail 0
ℹ duration_ms 183ms

🤖 Generated with Claude Code

Alex Jones and others added 2 commits February 28, 2026 14:52
- Restrict CORS to localhost origins only (was wildcard *)
- Add request body size limits (1MB default, 8KB for input endpoints)
  to prevent memory exhaustion DoS
- Fix path traversal in static file serving via path.resolve containment
- Prevent AppleScript injection by validating TTY paths against /^\/dev\/ttys\d+$/
- Improve AppleScript escaping to handle newlines, carriage returns, tabs
- Harden team name validation with strict allowlist regex [a-zA-Z0-9_-]
  plus defense-in-depth path containment check
- Add input validation/truncation to event receiver (type, sessionId,
  message, timestamp format, metadata size)
- Reject unexpected keys in config PATCH endpoint
- Set restrictive file permissions (0o700 dirs, 0o600 files) on
  ~/.conductor/ config and database
- Add security headers: X-Content-Type-Options, X-Frame-Options, CSP

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract security utilities (CORS, readBody, serveStatic) into
server/security.ts for testability. Extract sanitizeEventBody from
event-receiver.ts to enable DB-free unit testing.

New test files:
- server/security.test.ts (28 tests)
  - CORS origin allowlist (allowed/blocked/missing origins)
  - Security headers (CSP, X-Frame-Options, nosniff)
  - readBody size limits (normal, chunked, empty, exact limit, overflow)
  - serveStatic path traversal (../, internal .., SPA fallback, MIME types)

- server/actions/cleanup.test.ts (18 tests)
  - Valid team deletion with hyphens/underscores/digits
  - Path traversal prevention (../, /, ..)
  - Special character rejection (spaces, semicolons, backticks,
    dollar signs, null bytes, newlines, backslashes, unicode)

- server/hooks/event-receiver.test.ts (29 tests)
  - Normal input passthrough
  - Missing field defaults (type, sessionId, timestamp, message, project)
  - Field truncation (type@128, sessionId@256, message@1024, project@256)
  - Timestamp validation (valid ISO, invalid, empty, non-string)
  - Metadata size limit (small preserved, >8KB dropped)
  - Type coercion (non-string inputs default correctly)

All 136 tests pass (75 new + 61 existing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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