Skip to content

fix: resolve AppleScript command injection and escape order bugs (#5)#10

Open
nandanadileep wants to merge 1 commit into
ethanplusai:mainfrom
nandanadileep:fix/issue-5-applescript-injection
Open

fix: resolve AppleScript command injection and escape order bugs (#5)#10
nandanadileep wants to merge 1 commit into
ethanplusai:mainfrom
nandanadileep:fix/issue-5-applescript-injection

Conversation

@nandanadileep
Copy link
Copy Markdown
Contributor

Summary

Fixes all four vulnerabilities reported in issue #5.

  • Bug 1 (CRITICAL) — open_terminal() injection: Added _applescript_escape() helper in actions.py that escapes backslashes first, then double-quotes, and strips newlines/carriage returns before any user-controlled string is embedded in a do script AppleScript call.
  • Bug 2 (CRITICAL) — prompt_existing_terminal() injection: Same fix — both project_name and prompt now go through _applescript_escape() before use in keystroke and window-name matching.
  • Bug 3 (HIGH) — --dangerously-skip-permissions hardcoded: Removed all unconditional uses across actions.py, qa.py, work_mode.py, and server.py. The flag is now only appended when JARVIS_SKIP_PERMISSIONS=true is set in the environment. Documented in .env.example.
  • Bug 4 (MEDIUM) — escape order in mail_access.py: Fixed search_mail() and read_message() to replace \ before ", preventing the \"\\" double-escape corruption.

Test plan

  • All 26 existing tests pass: python3 -m pytest tests/test_e2e_pipeline.py tests/test_feedback_loop.py -v
  • No conflicts with upstream main
  • _applescript_escape() correctly handles backslashes, double-quotes, and newlines
  • JARVIS_SKIP_PERMISSIONS env var controls the dangerous flag across all call sites

ethanplusai#5)

- Bug 1 & 2: Add _applescript_escape() helper in actions.py that escapes
  backslashes, double-quotes, and strips newlines before embedding user
  input into AppleScript do script / keystroke calls
- Bug 3: Gate --dangerously-skip-permissions behind JARVIS_SKIP_PERMISSIONS
  env var across actions.py, qa.py, work_mode.py, and server.py; document
  the new var in .env.example
- Bug 4: Fix escape order in mail_access.py search_mail() and read_message()
  so backslashes are replaced before double-quotes, preventing \" → \\"
@ethanplusai
Copy link
Copy Markdown
Owner

Thanks @nandanadileep — your fix is correct and tests pass cleanly. I've taken this over in #11 to layer on a few additional escape sites we found while reviewing (_focus_terminal_window, _run_task, handle_build, an "open last project" Finder call, and api_fix_self), promote _applescript_escape to a shared helper that server.py imports, and add a unit test suite for the escape function.

Your original commit is preserved as the first commit on the new branch with full authorship, and you're credited as Co-Authored-By on the follow-up commit.

Closing this in favor of #11 — really appreciate the contribution and the surgical, well-scoped fix. 🙏

ethanplusai added a commit that referenced this pull request May 15, 2026
fix: AppleScript injection hardening (supersedes #10)
pull Bot pushed a commit to RealShocky/jarvis that referenced this pull request May 15, 2026
Builds on the AppleScript injection fix from PR ethanplusai#10. While reviewing,
found additional unescaped interpolation sites that should also use the
shared escape helper:

- _focus_terminal_window: was using the same incomplete quote-only escape
  pattern that PR ethanplusai#10 fixed elsewhere in actions.py
- _run_task: {work_dir} interpolated raw into do script
- handle_build: {path} interpolated raw into do script
- "open last project" fallback: {last["path"]} interpolated raw into a
  Finder open POSIX file call
- api_fix_self: {jarvis_dir} interpolated raw (low risk since derived
  from __file__, but escape for consistency)

Also:
- Renamed actions._applescript_escape -> actions.applescript_escape
  (drop underscore) so server.py can import it as a shared helper
  instead of duplicating the implementation
- Added tests/test_applescript_escape.py covering backslash, quote,
  newline/CR, ordering, and a representative injection payload

Note on upgrade behavior: PR ethanplusai#10 flips the default for
--dangerously-skip-permissions from "always on" to "off unless
JARVIS_SKIP_PERMISSIONS=true". Existing users who upgrade without
setting that env var will see Claude permission prompts on each
subprocess (which may appear to hang in JARVIS's voice-driven flow).
Set JARVIS_SKIP_PERMISSIONS=true to preserve the previous behavior.

Co-Authored-By: Nandana Dileep <nandanadileep29@gmail.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.

2 participants