From 47c5a8b0b9c98e73c9d8c7089faa26d1e63652e1 Mon Sep 17 00:00:00 2001 From: Mortdecai Date: Mon, 28 Sep 2026 16:35:59 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20session=20handoff=20=E2=80=94=20travers?= =?UTF-8?q?al=20fix,=20write-back,=20MCP=20registration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- ...09-28-163442-security-fix-and-writeback.md | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 .claude/handoffs/2026-09-28-163442-security-fix-and-writeback.md diff --git a/.claude/handoffs/2026-09-28-163442-security-fix-and-writeback.md b/.claude/handoffs/2026-09-28-163442-security-fix-and-writeback.md new file mode 100644 index 0000000..c9f1d26 --- /dev/null +++ b/.claude/handoffs/2026-09-28-163442-security-fix-and-writeback.md @@ -0,0 +1,128 @@ +# Handoff: workbench-server — path traversal fixed, page write-back added, MCP registered + +## Session Metadata +- Created: 2026-09-28 16:34:42 +- Project: /home/claude/bin/workbench-server +- Branch: master (pushed to git.sethpc.xyz/Seth/workbench-server) +- Session duration: ~1.5 h (shared with IO-102 work) + +### Recent Commits (for context) + - a6766e8 docs: document page write-back via POST /log + - 05fcb7f feat: POST /log from the page + load state.json on page open + - 6562250 fix: block path traversal in static handler + - 4b55135 docs: add open source subheading + - c97adfc docs: README and INSTALL.md — AI-guided setup with SSH ControlMaster + +## Handoff Chain + +- **Continues from**: None (first handoff; earlier history lives in `~/bin/claude_analog/SESSION.md`, "Workbench MCP Server" section) +- **Supersedes**: None + +## Current State Summary + +The workbench MCP server was already built but had never been used live. This session gave it a debug run: tests passed and scaffold/HTTP/WS/log/stop worked, but the MCP never loaded because it was only listed under `mcpServers` in `~/.claude/settings.json`, which Claude Code ignores. It is now registered at user scope (`claude mcp add --scope user workbench -- /home/claude/.local/bin/workbench mcp`) and shows ✔ Connected. Along the way I found and fixed a LAN-exposed path traversal (any file readable by `claude` was servable). I also added `POST /log` so pages can record data, and made the scaffold load `state.json` when a page opens. 18/18 tests pass. First real project: `~/workbench/io102` (see `~/bin/claude_analog/.claude/handoffs/`). + +## Codebase Understanding + +### Architecture Overview + +- `src/workbench/server.py`: `WorkbenchServer` (testable core) + `create_mcp_server` (FastMCP wrapper, 6 tools). Each project gets its own aiohttp app on a port from 8070 up: `/ws`, `POST /log`, static files under the project dir. +- `src/workbench/project.py`: on-disk layout under `~/workbench//`: `index.html` (from scaffold), `state.json`, `session.md`/`.jsonl`, `cost-log.jsonl`, `.server.json` (pid/port). +- `src/workbench/scaffold.html`: WS client. State keys are `template` (HTML), `styles`, `script` (run via `new Function`). On load: fetch `state.json`, fall back to localStorage. +- Editable pip install (`~/.local/bin/workbench` → this source tree), so source edits apply on the next MCP start. + +### Critical Files + +| File | Purpose | Relevance | +|------|---------|-----------| +| `src/workbench/server.py` | HTTP/WS/MCP | Static handler now resolves paths and requires `is_relative_to(root)` | +| `src/workbench/scaffold.html` | Browser client | Log feed renders as text (browsers can now POST entries) | +| `tests/test_server.py` | Includes live-server tests for traversal and POST /log | | + +### Key Patterns Discovered + +- Tests use a `tmp_workbench` fixture; live-server tests call `server._start_http_server` directly and use aiohttp's ClientSession. + +## Work Completed + +### Tasks Finished + +- [x] Debug run: pytest, a live smoke test, and an MCP stdio handshake (all 6 tools listed) +- [x] Registered the MCP at user scope (`~/.claude.json` backed up to `~/.claude/.backup-claude.json.*` / `~/.claude/.backup/`) +- [x] Fixed path traversal (commit 6562250), with a test +- [x] Added POST /log + load-from-state.json + text-only log feed (05fcb7f), with a test +- [x] README note (a6766e8) + +### Files Modified + +| File | Changes | Rationale | +|------|---------|-----------| +| `src/workbench/server.py` | traversal guard; `log_handler` | security; page write-back | +| `src/workbench/scaffold.html` | fetch state.json on load; text-only log entries | new devices see content; no script injection via /log | +| `tests/test_server.py` | 2 tests | | +| `README.md` | write-back paragraph | | + +Backups: `src/workbench/.backup/`, `tests/.backup/`, `.backup/README.md.*` + +### Decisions Made + +| Decision | Options Considered | Rationale | +|----------|-------------------|-----------| +| Browser write-back via `POST /log` reusing `append_log` | A separate readings store | One log for humans and the AI; `workbench_read_log` already covers resume | +| Server for io102 was run as a session-scoped background task, not detached | nohup/setsid daemon | A detached server would block live pushes from a later MCP session (see gotcha) | + +## Pending Work + +### Immediate Next Steps + +1. Nothing blocking. Next real use is the IO-102 readings session (driven from `~/bin/claude_analog`). +2. Optional: fix reattach so live pushes work across processes (see gotcha). Simplest option: when `workbench_scaffold` finds a live server owned by another pid, have `workbench_state` POST the state to that server (e.g. a new `POST /state` endpoint) instead of broadcasting to an empty local client set. + +### Blockers/Open Questions + +- [ ] `rival-llms` MCP is also only in `settings.json`'s `mcpServers`, so it doesn't load either. Not touched; ask Seth whether he wants it. + +### Deferred Items + +- The FastMCP `serverInfo` version reports 1.26.0 (the SDK's version), not 0.2.0. Cosmetic. +- `workbench serve ` passes the name as the title, which overwrites index.html's title. Cosmetic. +- No favicon (a 404 in the browser console). Cosmetic. + +## Context for Resuming Agent + +### Important Context + +- The README claim "reattach… browser stays connected" is only half true. The reattaching process has an empty ws_clients set, so `workbench_state` writes state.json but browsers see it only after a refresh. +- `POST /log` has no auth. It's LAN-only, like the rest of the server. Anyone on the LAN can append log lines, but can't read files outside the project or inject script into the feed. + +### Assumptions Made + +- LAN trust model (no auth) is acceptable for Seth's homelab. + +### Potential Gotchas + +- Playwright's bundled browsers are not installed; use `chromium.launch(executable_path="/usr/bin/chromium")`. +- MCP tools only appear in Claude Code sessions started after registration. + +## Environment State + +### Tools/Services Used + +- `workbench` MCP (user scope), `gitea push` + +### Active Processes + +- None persistent. The io102 server from 2026-09-28 was session-scoped. + +### Environment Variables + +- None + +## Related Resources + +- `~/bin/claude_analog/.claude/handoffs/2026-09-28-163442-io102-focus-ready-for-readings.md` +- `docs/superpowers/plans/2026-03-30-workbench-v2.md` + +--- + +**Security Reminder**: Before finalizing, run `validate_handoff.py` to check for accidental secret exposure.