T09 · Insecure Skill Coding Practices
- Location
scripts/mac_use.py:545- Finding
AppleScript Injection Through Unescaped Keyboard Input
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Mac desktop-control skill is mostly purpose-aligned, but it needs broad screen and input permissions and has unsafe input, temporary-file, and install practices that deserve review before use.
Install only if you are comfortable granting the host process Screen Recording and Accessibility permissions, because the skill can see windows and send clicks or keystrokes to desktop apps. Avoid using it around passwords, private messages, financial apps, admin prompts, or irreversible actions. Prefer running it in a dedicated environment, use a virtualenv with pinned dependencies, and clear /tmp/mac_use*. files after use.
scripts/mac_use.py:545AppleScript Injection Through Unescaped Keyboard Input
scripts/mac_use.py:36Predictable Shared Temporary Files Allow Symlink and State-Tampering Attacks
requirements.txt:1Unpinned Dependencies Are Installed Into the Managed Python Environment
The skill exposes powerful shell-based GUI automation and writes artifacts to /tmp, but it declares no explicit tool scope or permission boundaries. In an agent ecosystem, that increases the chance the skill is invoked with broader-than-necessary authority, enabling unintended command execution, app control, screenshot capture of sensitive data, and interaction with user applications.
The activation condition is extremely broad—'interact with any Mac desktop application's graphical interface'—which can cause the skill to trigger for many unrelated requests. Because the skill can visually inspect windows, click, type, and send keys to arbitrary apps, overbroad activation materially raises the risk of misuse, accidental invocation, and unsafe handling of sensitive on-screen content.
This AppleScript call interpolates the untrusted app_name directly into a script string without escaping. A crafted app name containing quotes or AppleScript syntax could break out of the string literal and execute unintended AppleScript actions, which is especially dangerous in a GUI automation skill that already has permission to control the desktop.
def activate_app(app_name):
"""Bring an app to the foreground."""
subprocess.run(
["osascript", "-e", f'tell application "{app_name}" to activate'],
capture_output=True,
timeout=5,
The AXRaise AppleScript is assembled from app_name and window_title, and only the title is escaped while app_name remains unescaped. Because both values may originate from externally influenced GUI state, this creates an AppleScript injection surface that could trigger arbitrary automation actions on the host.
f'to perform action "AXRaise" of window "{safe_title}"'
)
try:
subprocess.run(["osascript", "-e", script], capture_output=True, timeout=5)
time.sleep(0.3)
except Exception:
pass
The skill captures full-screen content, crops application windows, performs OCR, and writes both the image and extracted text metadata to predictable files in /tmp without any user-facing consent, minimization, or cleanup. In the context of a desktop automation tool, this can expose secrets from emails, chats, documents, tokens, or other sensitive on-screen data to other local processes or later sessions.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def capture_full_screen():
"""Take a full-screen screenshot. Returns True on success."""
r = subprocess.run(
["/usr/sbin/screencapture", "-x", SCREENSHOT_FULL],
capture_output=True, timeout=10,
)
The skill can inject global keystrokes and keycodes through System Events without any safety gate, focus verification, or user confirmation. If the wrong window is focused or input is attacker-influenced, this can trigger destructive actions such as sending messages, approving prompts, closing documents, or executing shortcuts in privileged applications.
The keystroke_via_osascript helper inserts untrusted key text into an AppleScript string without escaping. An attacker controlling the combo or key value could inject additional AppleScript, leading to arbitrary GUI actions or command execution via AppleScript-capable apps.
else:
mod_str = ""
script = f'tell application "System Events" to keystroke "{key}"{mod_str}'
r = subprocess.run(["osascript", "-e", script], capture_output=True, timeout=10)
if r.returncode != 0:
sys.stderr.write(f"osascript keystroke failed: {r.stderr.decode().strip()}\n")
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
else:
mod_str = ""
script = f'tell application "System Events" to key code {code}{mod_str}'
r = subprocess.run(["osascript", "-e", script], capture_output=True, timeout=10)
if r.returncode != 0:
sys.stderr.write(f"osascript keycode failed: {r.stderr.decode().strip()}\n")
Typing is implemented by replacing the global clipboard and pasting, which silently destroys the user's existing clipboard contents and may leak sensitive data if the clipboard is monitored or later pasted elsewhere. In a GUI automation context this side effect is easy to miss and can affect unrelated applications and workflows.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if args.app:
activate_and_find(args.app)
proc = subprocess.Popen(["pbcopy"], stdin=subprocess.PIPE)
proc.communicate(args.text.encode("utf-8"))
keystroke_via_osascript("v", ["command"])
The dependency is specified with a lower bound only, which permits installation of any future release and prevents reproducible builds. This increases supply-chain risk because a compromised or breaking upstream release could be pulled in unexpectedly, though the requirement alone is not evidence of active exploitation.
pyobjc-framework-Vision>=12.0
pyautogui>=0.9
Pillow>=10.0
Using an unpinned version for pyautogui creates a supply-chain and reproducibility weakness by allowing arbitrary newer releases to be installed. In this skill's context, pyautogui directly automates GUI input, so a malicious or compromised upstream release would have powerful interaction capability on the host desktop.
pyobjc-framework-Vision>=12.0
pyautogui>=0.9
Pillow>=10.0
Pillow is unpinned, so installs are non-deterministic and may resolve to vulnerable or compromised releases. Because this skill processes screenshots and images, Pillow is part of the active data path, making dependency integrity and version control more important than in an unused transitive package.
pyobjc-framework-Vision>=12.0
pyautogui>=0.9
Pillow>=10.0
The manifest does not pin Pillow, and Pillow has multiple published advisories, so the installed version could be affected without any way to verify from this file alone. In a GUI automation skill that captures and handles screenshots, a vulnerable imaging library increases risk of code execution or denial of service through crafted image inputs or unsafe parsing paths.
The Vision text recognition request forces recognition languages to zh-Hans and en only. This imposes a language/locale constraint without offering user opt-in or documenting why the tool is limited to those languages, which matches the policy's language-choice concern.
No suspicious patterns detected.