Skip to content

security(headers): CSP, escapeHtml quote fix, JSON config injection#11

Open
BRlin-o wants to merge 4 commits into
lis186:mainfrom
BRlin-o:security/csp-headers
Open

security(headers): CSP, escapeHtml quote fix, JSON config injection#11
BRlin-o wants to merge 4 commits into
lis186:mainfrom
BRlin-o:security/csp-headers

Conversation

@BRlin-o

@BRlin-o BRlin-o commented Apr 17, 2026

Copy link
Copy Markdown

Summary

Dashboard security hardening. Part 3 of 3 PRs from the audit in #7.
Depends on #9 and #10.

  • Security headers — CSP, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer on all responses
  • JSON config injection — replace <!--__PROXY_CONFIG__--> inline <script> with <script type="application/json"> + JSON.parse. No more executable JS injected into HTML.
  • escapeHtml quote fix — add "&quot; and '&#39; (prevents XSS via attribute injection, e.g. model names containing quotes)
  • CCXRAY_CSP=strict — opt-in env to drop 'unsafe-inline' from script-src. Currently the dashboard has ~30 inline on*= handlers that need refactoring first (tracked as future work).

CSP policy

default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';
img-src 'self' data:; connect-src 'self'; frame-ancestors 'none';
base-uri 'none'; form-action 'self'

style-src 'unsafe-inline' kept because the dashboard has extensive dynamic style="width:..." — converting to CSS classes is cosmetic, not a security concern.

Test plan

  • 253 tests pass, 0 failures (matches full security-fixes branch)
  • test/csp-headers.test.js (4 cases): CSP header present, nosniff, no-referrer, JSON config block without inline assignment
  • Manual: dashboard loads correctly with no CSP violations in console
  • Manual: all interactive features (expand/collapse, intercept, system-prompt viewer) still work

Ref: #7

🤖 Generated with Claude Code

BRlin-o and others added 4 commits April 17, 2026 12:16
Network surface reduction (Phase 0):
- Bind 127.0.0.1 by default; add HOST env for opt-in network access
- Cap request body at 50 MB (CCXRAY_MAX_BODY_MB); return 413 on exceed
- Per-IP SSE connection limit (default 8, CCXRAY_SSE_MAX_PER_IP)
- HTTP timeouts: headersTimeout=60s, requestTimeout=120s, keepAliveTimeout=5s
- Sanitize ANSI/control chars in logged request URLs (log injection)
- Extract readBodyCapped() helper for safe body accumulation

Filesystem hardening (Phase 1):
- Log files 0o600, log directories 0o700; chmod on existing paths
- safeJoin() rejects path traversal (../, /, \, null bytes)
- Hub lockfile and log written with 0o600

Hub localhost-only + validation (Phase 2):
- /_api/hub/* restricted to loopback IPs (127.0.0.1, ::1, ::ffff:127.0.0.1)
- /_api/health remains open (pure liveness, no sensitive info)
- Register/unregister validate pid (int, 0 < n ≤ 2²²) and cwd (string, ≤ 4096)
- Hub body cap 1 KB for control endpoints

BREAKING: Server now binds 127.0.0.1 only. Set HOST=0.0.0.0 for LAN access.

Closes lis186#7 (partial)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bumps basic-ftp to patched version, resolving HIGH severity
CRLF injection in @aws-sdk transitive dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace === with crypto.timingSafeEqual for bearer token comparison;
  constant-time even on length mismatch
- Remove ?token= query param auth (leaks in browser history, Referer, logs)
- Add POST /login + POST /logout with HttpOnly cookie session
  (SameSite=Strict, Secure on non-loopback, 7-day TTL)
- HTML Accept requests get 302 to /login.html; API requests get 401 JSON
- Add login.html / login.js — minimal token input form
- Per-process HMAC secret (sessions invalidate on restart — acceptable
  for a local dev tool)

BREAKING: ?token=XXX query auth removed. Use Authorization: Bearer header
(CLI/curl) or the new /login.html flow (browser).

Closes lis186#7 (partial)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Content-Security-Policy, X-Content-Type-Options: nosniff,
  Referrer-Policy: no-referrer to all responses via writeHead wrapper
- script-src keeps 'unsafe-inline' by default (~30 inline on*= handlers);
  CCXRAY_CSP=strict env opts into strict mode for future handler refactor
- Replace <!--__PROXY_CONFIG__--> inline script injection with
  <script type="application/json"> + JSON.parse (no executable JS)
- escapeHtml now escapes " → &quot; and ' → &lis186#39; (attribute injection fix)

Closes lis186#7

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