T09 · Insecure Skill Coding Practices
- Location
scripts/cursor_bga.py:140- Finding
Cursor API Key Exposed Through Detached Process Command-Line Arguments
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is mostly aligned with managing Cursor Cloud Agents, but it starts an under-disclosed background watcher that carries the Cursor API key and can send task status or conversation excerpts to Feishu.
Review carefully before installing. Use only with repositories where Cursor has the minimum needed access, avoid passing the API key via --api-key, prefer a tightly permissioned key file or a secure credential store, do not persist the key in shell startup files, and avoid Feishu notifications for sensitive work. Be aware that creating an agent can start a detached watcher that keeps using the Cursor API key after the visible command returns.
scripts/cursor_bga.py:140Cursor API Key Exposed Through Detached Process Command-Line Arguments
The Feishu messaging helper is an explicit exfiltration mechanism: it sends agent-related data off-host to a third-party communication platform. In this skill, that becomes materially risky because later code uses it to send agent status, branch/PR metadata, and failure-context conversation content, none of which are clearly constrained to non-sensitive data.
def _send_feishu(message: str, target: str = ""):
"""Send message to Feishu via openclaw CLI."""
import subprocess
resolved_target = target or os.environ.get("CURSOR_NOTIFY_TARGET", "")
if not resolved_target:
The skill describes capabilities that involve shell execution, reading environment/file-based secrets, and outbound network access, but it does not declare any explicit tool scope or permission boundaries. This increases the chance that an agent can invoke powerful actions without clear user-visible constraints, especially because the skill is designed to launch remote coding agents and manage repositories.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
description: >-
Launch and manage Cursor Cloud Agents via the official API v0.
Use when user asks to delegate a coding task to Cursor's cloud agent,
create a PR automatically, or track agent progress.
Requires python3 and a Cursor API key stored in ~/.cursor_api_key.
---
The skill explicitly supports auto-execution and later states it appends an instruction to 'execute directly' so the remote agent does not wait for confirmation. That reduces human oversight for code changes, testing, and PR creation in a remote environment, making prompt mistakes or malicious instructions more likely to result in unintended repository changes.
| Command | Description |
|---------|-------------|
| `create` | Launch a new Cloud Agent (`--wait` to block until done, `--no-direct` to skip auto-execute hint) |
| `list` | List recent agents (filter by PR URL, limit) |
| `get` | Get agent details (status, summary, PR info) |
| `check` | Poll agent until finished, print final summary |
The command set includes a permanent delete operation and a stop operation, but the skill does not prominently require confirmation or warn about irreversible effects at the point those actions are described. In an agentic workflow, lack of confirmation around destructive state changes can lead to accidental loss of agent history or interruption of active work.
The workflow instructs the operator to run create --wait --auto-pr, and the surrounding text explains that the script adds a 'execute directly' instruction to prevent confirmation. This creates a persistent autonomous workflow that can continue making code changes and potentially open a PR without further user interaction, increasing the blast radius of a bad prompt or mistaken target repo.
### Phase 2: Execute
5. Run `create --wait --auto-pr` with the confirmed repo and prompt
6. The script auto-appends a "execute directly" instruction to prevent agent from asking for confirmation (use `--no-direct` to disable)
7. `--wait` blocks until agent finishes and prints a full summary including conversation excerpt
Large portions of the Constraints and Troubleshooting sections are written only in Chinese, and line L139 states language behavior tied to the prompt without offering a user-facing language selection policy. This creates a locale/language policy issue because the skill documentation effectively assumes Chinese for key operational guidance rather than explicitly offering a choice or documenting a justified locale restriction.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
If you haven't already:
1. In the Cursor Dashboard, go to **Settings** or **Integrations**
2. Connect your **GitHub** (or GitLab) account
3. Grant read-write permissions to the repos you want agents to work on
## Step 3 — Save API Key
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# Method A — File (recommended)
echo 'your_api_key_here' > ~/.cursor_api_key
chmod 600 ~/.cursor_api_key
# Method B — Environment variable
export CURSOR_API_KEY='your_api_key_here'
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# Method A — File (recommended)
echo 'your_api_key_here' > ~/.cursor_api_key
chmod 600 ~/.cursor_api_key
# Method B — Environment variable
export CURSOR_API_KEY='your_api_key_here'
The documentation explicitly instructs users to persist a long-lived API key in shell startup files for persistence, which stores the credential in plaintext on disk and may expose it through backups, local compromise, dotfile syncing, or accidental disclosure. In the context of this skill, the key grants access to Cursor Cloud Agents and may enable delegated code actions and repository access, making plaintext persistence materially risky.
Telling users to add the exported API key to ~/.bashrc or ~/.zshrc creates session persistence for a sensitive credential and leaves it in plaintext in a commonly accessed configuration file. That increases the chance of credential theft via local compromise, shell history mistakes, dotfile repositories, backups, or shared account access.
export CURSOR_API_KEY='your_api_key_here'
## Step 4 — Verify
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
cursor-agent skill: Manage Cursor Cloud Agents via the official API v0.
Usage:
cursor_bga.py create --repo owner/repo --prompt "task" [--ref main] [--model MODEL] [--auto-pr] [--branch NAME] [--wait] [--no-direct]
cursor_bga.py list [--limit N] [--pr-url URL]
cursor_bga.py get --agent-id ID
cursor_bga.py conversation --agent-id ID
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import urllib.request
import urllib.error
API_BASE = "https://api.cursor.com/v0"
KEY_FILE = os.path.expanduser("~/.cursor_api_key")
DIRECT_SUFFIX = "\n\n请直接执行修改并提交代码,不需要等待确认,不要只输出方案。"
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import urllib.request
import urllib.error
API_BASE = "https://api.cursor.com/v0"
KEY_FILE = os.path.expanduser("~/.cursor_api_key")
DIRECT_SUFFIX = "\n\n请直接执行修改并提交代码,不需要等待确认,不要只输出方案。"
The code appends a Chinese instruction string to prompts by default, and later sends Chinese-language notification messages. This imposes a specific language on users and downstream agent interactions without any documented locale selection or opt-in, which matches the language/locale policy violation criteria.
The skill description focuses on launching and managing Cursor agents, but the implementation also sends Feishu notifications and later opens browsers. That hidden expansion of scope matters because it can transmit agent metadata/content to third-party services without clear user expectation, increasing the risk of unintended data disclosure.
The code invokes unrelated local CLIs (openclaw and gh) that are not necessary for the core Cursor API management function. This expands the trust boundary to additional local tooling, increasing the chance of data leakage, environment-dependent behavior, and abuse if those binaries are replaced or misconfigured.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"--message", message,
]
try:
result = subprocess.run(cmd, timeout=30, capture_output=True, text=True)
return result.returncode == 0
except Exception:
return False
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
"--message", message,
]
try:
result = subprocess.run(cmd, timeout=30, capture_output=True, text=True)
return result.returncode == 0
except Exception:
return False
The detached watcher persists after the main command exits, continues polling the remote service, and can later transmit status and conversation excerpts externally. Persistent background behavior is more dangerous in an agent skill because it operates outside the user's immediate awareness and keeps using credentials after the user-facing action appears complete.
This finding is valid because the skill deliberately spawns a background process that survives beyond the initiating command and continues operating with access to the API key and notification channel. While not classic host persistence, it is session persistence in the sense of continued autonomous activity outside immediate user control, which is risky in an agent-integration skill.
def _spawn_background_watcher(agent_id: str, api_key: str, agent_url: str, name: str, notify_target: str = ""):
"""Spawn a background process that polls until done, then notifies Feishu."""
import subprocess, sys, textwrap
watcher_code = textwrap.dedent(f"""
The watcher contains another external transmission path to the Cursor API, but unlike the main flow it runs silently in a detached background process with the API key embedded in its source. The danger is not the API destination alone; it is the hidden, persistent, credential-bearing network activity occurring after the main command returns.
watcher_code = textwrap.dedent(f"""
import time, json, urllib.request, urllib.error, base64, subprocess, shutil, os
API_BASE = "https://api.cursor.com/v0"
agent_id = {repr(agent_id)}
api_key = {repr(api_key)}
agent_url = {repr(agent_url)}
On failure or stop, the watcher fetches the agent conversation and sends up to 200 characters of the last message to Feishu. Agent conversations may contain source code, secrets, internal URLs, or user prompts, so this creates a concrete exfiltration path to a third-party messaging system without a strong user-facing warning at the point of transmission.
The script spawns a detached background Python process and embeds the Cursor API key directly into the generated watcher source passed on the command line. On many systems, command-line arguments for running processes are visible to other local users via process inspection tools, which can expose the API key. Because the child persists after the parent exits and runs silently, this also reduces user visibility and control over the credential-bearing process.
pass
""")
subprocess.Popen(
[sys.executable, "-c", watcher_code],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
No suspicious patterns detected.