Files
meshcore-hub/.plans/2026/03/09/01-security-fixes/summary.md

5.3 KiB

Phase Summary

Phase: .plans/2026/03/09/01-security-fixes Generated by: /jp-summary

Project Overview

This phase addresses CRITICAL and HIGH severity vulnerabilities identified in a security audit of MeshCore Hub. The fixes span stored XSS in server-rendered and client-side code, timing attacks on authentication, proxy header forgery, and a legacy endpoint with missing authentication. All changes are backward-compatible and preserve existing API contracts.

Goals

  • Eliminate all CRITICAL and HIGH severity security vulnerabilities found in the audit
  • Harden API key comparison against timing side-channel attacks
  • Prevent XSS vectors in both Jinja2 templates and client-side JavaScript
  • Add configurable proxy trust to defend against header forgery while maintaining backward compatibility
  • Remove the redundant legacy HTML dashboard endpoint that lacks authentication

Task Execution

Overview

Metric Value
Total tasks 12
Completed 12
Failed 0
Blocked 0
Skipped 0

Task Details

ID Title Role Complexity Status
TASK-001 Remove legacy HTML dashboard endpoint python small completed
TASK-002 Replace API key comparisons with constant-time comparison python small completed
TASK-003 Add WEB_TRUSTED_PROXY_HOSTS configuration setting python small completed
TASK-004 Integrate trusted proxy hosts into web app middleware and add startup warning python medium completed
TASK-005 Escape config JSON in template script block to prevent XSS breakout python small completed
TASK-006 Fix stored XSS in admin node-tags page frontend medium completed
TASK-007 Fix stored XSS in admin members page frontend small completed
TASK-008 Write tests for legacy dashboard endpoint removal python small completed
TASK-009 Write tests for constant-time API key comparison python small completed
TASK-010 Write tests for trusted proxy hosts configuration and startup warning python medium completed
TASK-011 Write tests for config JSON script block escaping python small completed
TASK-012 Update documentation for WEB_TRUSTED_PROXY_HOSTS setting docs small completed

Requirement Coverage

Metric Value
Total PRD requirements 7
Requirements covered by completed tasks 7
Requirements with incomplete coverage 0

All functional requirements (REQ-001 through REQ-005) and non-functional requirements (REQ-006, REQ-007) are fully covered by completed tasks.

Files Created and Modified

Created

  • tests/test_web/test_app.py

Modified

  • src/meshcore_hub/api/routes/dashboard.py
  • src/meshcore_hub/api/auth.py
  • src/meshcore_hub/api/metrics.py
  • src/meshcore_hub/common/config.py
  • src/meshcore_hub/web/app.py
  • src/meshcore_hub/web/static/js/spa/pages/admin/node-tags.js
  • src/meshcore_hub/web/static/js/spa/pages/admin/members.js
  • tests/test_api/test_dashboard.py
  • tests/test_api/test_auth.py
  • tests/test_common/test_config.py
  • README.md
  • AGENTS.md
  • PLAN.md

Review Rounds

Overview

Metric Value
Total review rounds 1
Total issues found 2
Issues fixed 2
Issues deferred 0
Issues remaining 0
Regressions introduced 0

Round Details

Round 1 (scope: full)

  • Issues found: 2 (0 CRITICAL, 0 MAJOR, 2 MINOR)
  • Issues fixed: 2 (both MINOR issues were addressed post-review)
  • Exit reason: success (no CRITICAL or MAJOR issues)

Known Issues and Deferred Items

No known issues. Both MINOR issues identified in the code review were addressed:

  • ISSUE-001 (MINOR, integration) -- Startup warning for proxy hosts used settings.web_admin_enabled instead of the effective admin_enabled value. Fixed by computing effective_admin before the warning check.
  • ISSUE-002 (MINOR, style) -- unsafeHTML() calls on pre-escaped data lacked explanatory comments. Fixed by adding inline HTML comments explaining that dynamic values are pre-escaped.

Decisions

  • Truthiness guards for hmac.compare_digest() -- Added read_key and ... / admin_key and ... guards in require_read because either key can be None when only one is configured, and hmac.compare_digest() raises TypeError on None arguments. This ensures the existing behavior of accepting either key type when configured.
  • unsafeHTML() retained with escapeHtml() pre-processing -- The unsafeHTML() calls in admin JS pages were retained because translation strings contain intentional HTML formatting tags (e.g., <strong>). API-sourced data is escaped before interpolation, making this pattern safe.
  • innerHTML retained for tag delete confirmation -- The delete confirmation in node-tags.js uses innerHTML because the translation template includes <span> formatting. The dynamic tag key is escaped with escapeHtml() before interpolation.

Suggested Next Steps

  1. Run full manual testing of admin pages (node-tags, members) with XSS payloads to verify fixes in a browser environment.
  2. Test WEB_TRUSTED_PROXY_HOSTS with a real reverse proxy (Traefik/Nginx) to verify proxy header trust restriction works as expected.
  3. Push commits and create a pull request for merge into main.