Security hotfix for the dashboard SPA catch-all route (GitHub issue #13).
- SPA static path is now bounded to
dashboard/dist— the catch-all inserver.pychecked containment with a plain stringstartswith, which has no path-component boundary: a sibling directory whose name merely shares the prefix (dist-x/) passed the check. Containment is nowPath.is_relative_toin a dedicated_resolve_static_filehelper. Paths that escape the dist root return 404 as before; client-side routes still fall back toindex.html.
Scope: only the REST server, and only when the built dashboard (dashboard/dist) is present. The MCP stdio server does not serve static files. 13 new regression tests in tests/test_spa_path_traversal.py.
Thanks to the reporter of #13. Full details in CHANGELOG.md.