fix(security): require dashboard auth for plugin API routes

Remove the blanket /api/plugins/* exemption from auth_middleware so
plugin API routes (e.g. Kanban dashboard) require the same session
token as all other /api/ endpoints.

Fixes #19533
This commit is contained in:
liuhao1024 2026-05-04 14:02:04 +08:00 committed by Teknium
parent 7312f7f849
commit ec9329ec41
2 changed files with 44 additions and 1 deletions

View file

@ -225,7 +225,7 @@ async def host_header_middleware(request: Request, call_next):
async def auth_middleware(request: Request, call_next):
"""Require the session token on all /api/ routes except the public list."""
path = request.url.path
if path.startswith("/api/") and path not in _PUBLIC_API_PATHS and not path.startswith("/api/plugins/"):
if path.startswith("/api/") and path not in _PUBLIC_API_PATHS:
if not _has_valid_session_token(request):
return JSONResponse(
status_code=401,

View file

@ -1826,6 +1826,49 @@ class TestNormaliseThemeExtensions:
assert r["componentStyles"]["card"] == {"opacity": "0.8", "zIndex": "5"}
class TestPluginAPIAuth:
"""Tests that plugin API routes require the session token (issue #19533)."""
@pytest.fixture(autouse=True)
def _setup_test_client(self, monkeypatch, _isolate_hermes_home):
"""Create a TestClient without the session token header."""
try:
from starlette.testclient import TestClient
except ImportError:
pytest.skip("fastapi/starlette not installed")
import hermes_state
from hermes_constants import get_hermes_home
from hermes_cli.web_server import app, _SESSION_HEADER_NAME, _SESSION_TOKEN
monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", get_hermes_home() / "state.db")
self.client = TestClient(app)
self.auth_client = TestClient(app)
self.auth_client.headers[_SESSION_HEADER_NAME] = _SESSION_TOKEN
def test_plugin_route_requires_auth(self):
"""Plugin API routes should return 401 without a valid session token."""
# Use a known plugin route (kanban board)
resp = self.client.get("/api/plugins/kanban/board")
assert resp.status_code == 401
def test_plugin_route_allows_auth(self):
"""Plugin API routes should work with a valid session token."""
# This test verifies the fix doesn't break authenticated access.
# The kanban plugin may not be loaded in the test environment,
# so we accept 200 (plugin loaded) or 404 (plugin not mounted).
resp = self.auth_client.get("/api/plugins/kanban/board")
assert resp.status_code in (200, 404)
def test_plugin_post_requires_auth(self):
"""Plugin POST routes should return 401 without a valid session token."""
resp = self.client.post("/api/plugins/kanban/tasks", json={"title": "test"})
assert resp.status_code == 401
class TestDashboardPluginManifestExtensions:
"""Tests for the extended plugin manifest fields (tab.override,
tab.hidden, slots) read by _discover_dashboard_plugins()."""