Skip to content

fix(security+watcher): post-review hardening (path traversal, worktree regex, viewer watch leak) - #8

Merged
JeanBaptisteRenard merged 1 commit into
mainfrom
fix/post-review-hardening
May 23, 2026
Merged

fix(security+watcher): post-review hardening (path traversal, worktree regex, viewer watch leak)#8
JeanBaptisteRenard merged 1 commit into
mainfrom
fix/post-review-hardening

Conversation

@JeanBaptisteRenard

Copy link
Copy Markdown
Collaborator

Aggregates the security-relevant and correctness suggestions from the four code-reviewer agents that reviewed PR#47/#48/#49 today. None block functionality; each closes a small but real foothold.

Changes

1. Subagent IPC path-traversal guard

`read-subagent-jsonl` and `start-subagent-watch` took `parentSessionId`/`agentId` from the renderer and fed them to `path.join` without validation. A compromised renderer (or a bug in the cache payload) could traverse out of `/`. Adds `SAFE_ID_RE = /^[a-zA-Z0-9_-]+$/` plus a defence-in-depth check that the resolved jsonl path still lives under the cache row's folder.

2. Worktree name hardening

Adds `WORKTREE_NAME_BAD = /^-|^\.\.?$/` to reject `.` / `..` / leading-dash worktree names. Verifies `parentRepo` is absolute and not flag-like before passing to `git -C`.

3. Friendly git ENOENT error

When git isn't on PATH, `delete-worktree` previously returned `spawn git ENOENT` to the renderer. Now returns `git not found on PATH — install git and retry`.

4. Viewer watch leak drain

Every expanded Agent block with a live tail started an `fs.watchFile` poll but never stopped it when the viewer was dismissed (only on individual block collapse / completion event). Track watches in a module-level `activeViewerWatches` set; `hideAllViewers` / `showJsonlViewer` / `showSubagentTranscript` drain it via `drainViewerWatches`.

Verification

```
$ npm test

tests 32 / pass 32 / fail 0

$ npm run lint
✖ 206 problems (0 errors, 206 warnings)
```

206 warnings are pre-existing tech debt — no new lint errors.

Related

  • PR#48 upstream (`feat/subagent-observability`) was force-updated with 3 blockers fixed in the same review pass (`subagentIndex` destructure, `grid-view` IPC signature, `fs.unwatchFile` callback).

…drain viewer watches

Follow-up to the four PR reviews. Five independent improvements, none
strictly required but each shipped well-defined feedback from the agents:

- **Subagent IPCs path-traversal guard**: read-subagent-jsonl and
  start-subagent-watch took parentSessionId/agentId straight from the
  renderer and fed them to path.join. A compromised renderer (or a
  bug in cache payload) could traverse out of <projects>/<folder>.
  Add SAFE_ID_RE + a path.resolve()-based defence-in-depth check that
  the resolved jsonl path still lives under the cache row's folder.

- **Worktree name hardening**: the regex now also rejects names matching
  `.` / `..` / leading `-` (the latter to keep git from parsing the
  name as a flag in some contexts). Parent repo must be absolute and
  not flag-like.

- **Friendly git ENOENT error**: when git is absent from PATH,
  delete-worktree returned `spawn git ENOENT` to the renderer — opaque.
  Now returns "git not found on PATH — install git and retry".

- **Viewer watch leak**: stopWatch closures registered in
  activeViewerWatches; hideAllViewers / showJsonlViewer / showSubagentTranscript
  drain via drainViewerWatches. Previously every expanded Agent block
  with a live tail kept polling indefinitely after the viewer was
  dismissed (until app shutdown).

32 tests still pass, 0 lint errors.
@JeanBaptisteRenard
JeanBaptisteRenard merged commit 1acfc71 into main May 23, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant