Skip to content

Security fixes, bug fixes, and native macOS fallback toolkit#2

Open
Ginkgo-AI wants to merge 1 commit intodisler:mainfrom
Ginkgo-AI:security-fixes-and-fallback-toolkit
Open

Security fixes, bug fixes, and native macOS fallback toolkit#2
Ginkgo-AI wants to merge 1 commit intodisler:mainfrom
Ginkgo-AI:security-fixes-and-fallback-toolkit

Conversation

@Ginkgo-AI
Copy link

Summary

This PR addresses several security vulnerabilities and bugs found during a code review, and adds documentation for a native macOS fallback toolkit when the steer binary is unavailable.

Security fixes

  • Auth on listen server — all endpoints now require an X-API-Key header matching LISTEN_API_KEY in the environment. Without this, any host on the local network could submit jobs that execute arbitrary shell commands with --dangerously-skip-permissions.
  • Localhost binding — server binds to 127.0.0.1 by default instead of 0.0.0.0. Set LISTEN_HOST=0.0.0.0 to override intentionally.
  • Path traversal + job_id injectionjob_id is now validated against ^[0-9a-f]{8}$ with an additional is_relative_to(JOBS_DIR) check on every endpoint.
  • API key no longer in scrollbackANTHROPIC_API_KEY is injected via tmux setenv instead of embedded in the command string, so it doesn't appear in capture-pane output.
  • Secure temp filestempfile.mkstemp() with 0o600 permissions replaces predictable /tmp/steer-<id>.txt paths (eliminates TOCTOU window and world-readable files).
  • AppleScript injection — both worker.py and tmux.py now write commands to a temp shell script instead of embedding them in AppleScript strings, preventing injection via special characters in cwd or session names.
  • Prompt not exposed in process table — prompt is read from the job YAML by the worker instead of being passed as a CLI argument visible in ps aux.

Bug fixes

  • NameError crash on every jobanthropic_key was referenced before load_dotenv assigned it in worker.py, crashing every worker immediately.
  • Infinite job loop_wait_for_sentinel had no timeout; added JOB_TIMEOUT_SECONDS = 3600.
  • Hardcoded tmux path — replaced /opt/homebrew/bin/tmux with shutil.which("tmux") (consistent with drive).
  • YAML race condition — all job YAML reads/writes now use fcntl.flock for exclusive/shared locking.
  • Swift force-cast crashaxVal used as! AXValue which crashes on unexpected attribute types; changed to as? AXValue.
  • Incomplete JSON escapingClipboard.swift, Type.swift, Hotkey.swift only escaped " and \n; now use JSONSerialization which handles all control characters per spec.
  • Missing auth header in clientdirect/client.py now sends X-API-Key on all requests.

Agent improvements

  • Native macOS fallback toolkit — documented osascript, screencapture, pbpaste/pbcopy, curl, and open as first-class alternatives in steer/SKILL.md for environments where the steer binary is unavailable.
  • Mandatory cleanup — system prompt now explicitly marks cleanup as mandatory, requires a pre-task snapshot of running apps, and asks for a confirmation update when cleanup is done.
  • LISTEN_API_KEY in .env.sample — documents the new required env var.

Test plan

  • Start listen server (just listen) — verify it starts on 127.0.0.1:7600
  • just send "..." without LISTEN_API_KEY set — should get 500
  • just send "..." with wrong key — should get 401
  • just send "..." with correct key — job should start and complete
  • Verify ANTHROPIC_API_KEY does not appear in tmux scrollback during job execution
  • Try GET /job/../../../../etc/passwd — should return 400
  • Build steer and verify no Swift compiler errors from as? change

🤖 Generated with Claude Code

Security:
- Add X-API-Key auth to all listen server endpoints (LISTEN_API_KEY env var)
- Bind server to 127.0.0.1 by default instead of 0.0.0.0
- Validate job_id with strict ^[0-9a-f]{8}$ regex + path traversal check
- Inject ANTHROPIC_API_KEY via tmux setenv instead of embedding in command string
- Use tempfile.mkstemp (O_EXCL, 0o600) for secure temp file creation
- Fix AppleScript injection in tmux.py and worker.py via temp shell script approach
- Pass prompt via YAML instead of argv (prevents ps aux exposure)

Bug fixes:
- Fix NameError: anthropic_key referenced before assignment in worker.py
- Add 1-hour job timeout to _wait_for_sentinel (was infinite loop)
- Use shutil.which("tmux") instead of hardcoded /opt/homebrew/bin/tmux
- Add fcntl file locking to all job YAML reads/writes
- Fix force-cast crash: axVal uses as? instead of as! in AccessibilityTree.swift
- Fix incomplete JSON escaping in Clipboard.swift, Type.swift, Hotkey.swift
- Add X-API-Key header to direct/client.py requests

Agent improvements:
- Document native macOS fallback toolkit in steer SKILL.md (osascript,
  screencapture, pbpaste/pbcopy, curl) for when steer binary is unavailable
- Make cleanup workflow mandatory with pre-task app snapshot and confirmation update
- Add LISTEN_API_KEY to .env.sample

Co-Authored-By: Claude Sonnet 4.6 <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