fix: block path traversal in static handler

The LAN-bound HTTP server joined the raw URL path onto the project dir,
so /../../.ssh/id_ed25519 (and %2e%2e variants) served any file the
user could read. Resolve and require the path stay inside the project.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mortdecai
2026-09-28 16:28:36 -04:00
parent 4b551351e1
commit 65622501fe
2 changed files with 25 additions and 2 deletions
+21
View File
@@ -103,3 +103,24 @@ async def test_reconnect_on_startup(tmp_workbench):
assert srv._active["live-proj"]["port"] == 8070
assert "dead-proj" not in srv._active
assert not (tmp_workbench / "dead-proj" / ".server.json").exists()
async def _live(server, name="p"):
import aiohttp
from workbench.project import create_project
create_project(name, "T", workbench_dir=server.workbench_dir)
port = await server._start_http_server(name)
return aiohttp.ClientSession(), f"http://127.0.0.1:{port}"
@pytest.mark.asyncio
async def test_static_blocks_path_traversal(server, tmp_workbench):
(tmp_workbench / "secret.txt").write_text("nope")
session, base = await _live(server)
async with session:
for evil in ["/../secret.txt", "/%2e%2e/secret.txt", "/..%2fsecret.txt"]:
async with session.get(base + evil, allow_redirects=False) as r:
assert r.status == 404, evil
async with session.get(base + "/") as r:
assert r.status == 200
await server.workbench_stop("p")