Skip to content

Fix XSS in side panel via unescaped server data (#245)#256

Open
boot-coco wants to merge 2 commits intodevelopfrom
fix/245-sidepanel-xss
Open

Fix XSS in side panel via unescaped server data (#245)#256
boot-coco wants to merge 2 commits intodevelopfrom
fix/245-sidepanel-xss

Conversation

@boot-coco
Copy link
Contributor

Summary

  • Enhanced escapeHtml() to also escape single quotes (''), closing a gap that allowed attribute injection via '-delimited contexts
  • Escaped all server-sourced values rendered via innerHTML in the side panel:
    • err.message in both loadItems() and loadThread() error handlers
    • item.type (both class attribute and text content), item.status, item.priority, item.created_by in list and thread views
    • item.id in data-id attribute
  • No functional changes — only security hardening

Impact

Prevents privilege escalation XSS where a malicious server response (or compromised data) could execute arbitrary JS in the extension's side panel context (which has Chrome extension privileges).

Test plan

  • Load side panel with normal data — verify items render correctly
  • Verify type/status badges still display with correct CSS classes
  • Test with item containing <script> in title, created_by, status fields — confirm no execution
  • Test error state — inject error with HTML in message, confirm it renders as text

Closes #245

🤖 Generated with Claude Code

boot-coco and others added 2 commits March 10, 2026 03:44
- Add single-quote escaping to escapeHtml() (&#39;)
- Escape err.message in error handlers (lines 119, 137)
- Escape item.type, item.status, item.priority, item.created_by in list and thread views
- Escape item.id in data-id attribute

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
V1 item handlers (handleGetItem, handleAddMessage, handleAssignItem,
handleResolveItem, handleVerifyItem, handleReopenItem, handleCloseItem,
handleRespondToItem) did not verify that the requested item belongs to
the authenticated user's app_id. This allowed any authenticated user to
read, modify, or close items belonging to other apps.

Add the same app_id scoping check used by V2 endpoints:
  if (req.v2Auth?.app_id && item.app_id !== req.v2Auth.app_id)
    → 403 Forbidden

For handlers that previously skipped the item fetch (assign, resolve,
verify, reopen, close), an explicit getItem() call is added before the
scope check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@jessie-coco jessie-coco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex Review R1: CLEAN — 0 P1 + 0 P2 + 2 P3

PR #256 final status:

  • 1 review round, R1 CLEAN
  • No bugs, no security issues found

panel.js — XSS fixes ✅

All innerHTML injections of server-sourced values are now wrapped in escapeHtml(). The updated escapeHtml() correctly covers all 5 OWASP-recommended characters (& < > " '). Verified:

  • err.message in both error handlers — escaped ✅
  • item.type, item.status, item.priority, item.created_by, item.id — all escaped ✅
  • priorityClass used as CSS class is derived from a local allowlist (['high', 'critical']) — safe without escaping ✅
  • time from formatTime() operates on Date output, not raw strings — safe ✅
  • Pre-existing escapeHtml() usage in renderDispatchBadges/renderDispatchDetails — verified correct ✅

server/index.js — app_id access control ✅

Cross-app access control (req.v2Auth?.app_id check) correctly added to all 8 item-mutation and item-read handlers. Pattern is consistent and correct — fetch item first, compare app_id, return 403 on mismatch.

P3 (non-blocking)

  1. Redundant 404 in handleAssignItem: After the new if (!item) return 404 + access control block, the subsequent if (!result.changes) return 404 is now unreachable (the item was already confirmed to exist). Same pattern in handleResolveItem, handleVerifyItem, handleReopenItem, handleCloseItem. Harmless — can clean up in a future PR if desired.

  2. handleGetItemsFull lacks explicit per-item access control: Pre-existing (not introduced by this PR). The list query filters by app_id so no actual cross-app leak, but adding the explicit check would be consistent with the pattern established here.

LGTM — approve.

@jessie-coco
Copy link
Contributor

@boot-coco Codex Review R1: CLEAN — approved. See review comments for details.

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.

2 participants