Skip to content

fix: close remaining shell-injection vectors in github-issues and git-diff#39

Open
kovtcharov wants to merge 1 commit intomainfrom
fix/shell-injection-followup
Open

fix: close remaining shell-injection vectors in github-issues and git-diff#39
kovtcharov wants to merge 1 commit intomainfrom
fix/shell-injection-followup

Conversation

@kovtcharov
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #38. That PR migrated most exec()execFile() but left four endpoints where user-controlled input was still interpolated into shell strings:

  • GET /api/workspaces/git-difffilePath wrapped in quotes only. Backticks / $() / \ in the query would execute.
  • GET /api/workspaces/github-issuesstate and assignee from the query string flowed unvalidated into gh issue list.
  • POST /api/workspaces/github-issuestitle / body only had " escaped; backticks, $(), and backslashes could still break out.
  • PATCH /api/workspaces/github-issues/:num — migrated as defense-in-depth (owner/repo came from a remote-URL regex).

All four now use execFile with an args array. Added:

  • state allowlist (open | closed | all)
  • assignee pattern (@me or a valid GitHub username)
  • .catch() on the post-spawn learnings / git-state promises in task-spawner.ts so a rejection no longer becomes an unhandledRejection.

Test plan

  • Type-check passes (tsc --noEmit)
  • git-diff?file=\whoami`.txt` → filename stays literal, no subshell
  • github-issues?state=hacker;rm -rf / → rejected with 400
  • gh issue create with title/body containing backticks and $() — should create issue with literal text (smoke test when gh-authed)
  • Existing frontend flows for git-diff / issues panel still render

🤖 Generated with Claude Code

…-diff

PR #38 migrated most exec() → execFile() but left four sites with
user-controlled input interpolated into shell strings:

- /api/workspaces/git-diff — filePath was wrapped in quotes only;
  shell metacharacters in the query parameter were interpreted.
- /api/workspaces/github-issues (GET) — state and assignee from the
  query string flowed unvalidated into the gh CLI invocation.
- /api/workspaces/github-issues (POST) — title and body only had "
  escaped; backticks, \$(), and backslashes could still inject.
- /api/workspaces/github-issues/:num (PATCH) — subcommand path
  migrated as defense in depth.

state is now allowlisted (open|closed|all) and assignee is restricted
to @me or a valid GitHub username pattern before reaching gh.

Also add .catch() handlers to the post-spawn learnings and git-state
promises in task-spawner so a rejection no longer surfaces as an
unhandledRejection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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