T09 · Insecure Skill Coding Practices
- Location
scripts/hinge_android.py:244- Finding
Gemini API Key Exposed Through Process Command-Line Arguments
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill does what it says, but it automates dating-app actions while uploading and retaining sensitive profile data with weak consent, secret-handling, and safety controls.
Review carefully before installing. Use only a dedicated emulator account, assume profile screenshots and generated summaries may be sent to Google Gemini and stored locally, avoid saving recordings unless needed, do not hardcode API keys in cron, and require explicit confirmation before any run that sends likes or comments.
scripts/hinge_android.py:244Gemini API Key Exposed Through Process Command-Line Arguments
SKILL.md:80Documentation Directs Users to Hardcode a Persistent API Credential in Cron Configuration
scripts/hinge_android.py:292Untrusted AI-Generated Comment Is Passed to an Android Shell with Incomplete Escaping
scripts/hinge_android.py:184Sensitive Dating-Profile Screenshots and Personal Summaries Are Retained Without Cleanup Controls
The skill description says it uses Gemini vision to evaluate dating profiles but does not clearly warn that profile images, prompts, and inferred attractiveness data may be transmitted to an external AI service. In this context, the omitted warning is significant because dating profiles contain sensitive personal data and users may not expect third-party processing.
The instructions recommend screen recording and pulling session videos to local storage without warning about the sensitivity of captured dating profiles and conversations. Those recordings and logs can contain personal images, bios, app identifiers, and behavioral data, making them highly privacy-sensitive if retained or shared insecurely.
Referenced artifact was not completely inspected
python3 scripts/hinge_android.py --likes 8 --user-desc "a 25yo tech guy in SF who's fit and active"
The skill sends screenshots and profile metadata from a dating app to an external AI provider, including personal images and inferred attributes, without any runtime consent prompt or minimization. In this context, the data is highly sensitive and third-party disclosure can violate privacy expectations, platform rules, or legal obligations.
The skill declares no explicit tool scope even though it clearly relies on shell execution, environment variables, and file writes. That mismatch weakens security boundaries and informed consent because an orchestrator or reviewer cannot easily tell that the skill can launch emulators, access secrets, and persist artifacts.
The invocation guidance is broad enough that ordinary conversation like 'check Hinge status' or 'manage daily dating app swiping' could trigger high-impact automation. Because this skill can drive an emulator, send likes, generate comments, and handle sensitive dating data, accidental invocation could cause unintended actions and privacy issues.
The skill directs collection and sharing of detailed summaries of liked and skipped profiles plus optional session video recordings. That creates a pipeline for storing and transmitting sensitive third-party personal data beyond what is necessary to automate likes, increasing privacy, compliance, and misuse risk.
The cron guidance omits any warning that hardcoding an API key creates credential exposure risks. In operational environments, cron definitions are often readable by admins, included in backups, and may be copied into tickets or docs, so the missing warning materially increases the chance of secret compromise.
Embedding a secret API key directly into cron payloads is unsafe because cron entries, scripts, and execution metadata can leak secrets to other users, support staff, backups, or logs. This is especially risky because the key grants access to an external AI service and could be abused for cost, data access, or service misuse.
The documentation explicitly tells users to hardcode the Gemini API key into cron payloads. Embedding secrets in cron commands can expose them through process listings, shell history, logs, configuration backups, and accidental sharing, creating a straightforward credential leakage path.
The analysis prompt instructs the model to evaluate profiles using language like 'Is she attractive?' and assumes the user is 'a guy on Hinge,' forcing a specific gendered framing without user choice. This is a natural-language policy issue because it imposes a fixed demographic/interaction model rather than offering configurable or neutral language.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def adb_cmd(*args):
try:
result = subprocess.run([ADB] + list(args), capture_output=True, text=True, timeout=30)
return result.stdout.strip()
except subprocess.TimeoutExpired:
log(f" ⚠️ ADB command timed out: {args}")
The script trusts the ADB executable path from environment or CLI and executes it directly. If an attacker can influence ADB_PATH or --adb, they can cause arbitrary local program execution with the privileges of the agent running the skill.
def adb_cmd(*args):
try:
result = subprocess.run([ADB] + list(args), capture_output=True, text=True, timeout=30)
return result.stdout.strip()
except subprocess.TimeoutExpired:
log(f" ⚠️ ADB command timed out: {args}")
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
def screenshot(path):
with open(path, "wb") as f:
subprocess.run([ADB, "exec-out", "screencap", "-p"], stdout=f, timeout=10)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def screenshot(path):
with open(path, "wb") as f:
subprocess.run([ADB, "exec-out", "screencap", "-p"], stdout=f, timeout=10)
def dump_ui():
This subprocess invocation inherits the same untrusted ADB executable path issue as adb_cmd(). A manipulated ADB value can replace the intended Android tooling with any local executable, leading to arbitrary code execution during screenshot capture.
def screenshot(path):
with open(path, "wb") as f:
subprocess.run([ADB, "exec-out", "screencap", "-p"], stdout=f, timeout=10)
def dump_ui():
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def dump_ui():
try:
adb_cmd("shell", "uiautomator", "dump", "/sdcard/ui_dump.xml")
subprocess.run([ADB, "pull", "/sdcard/ui_dump.xml", "/tmp/ui_dump.xml"],
capture_output=True, text=True, timeout=10)
with open("/tmp/ui_dump.xml") as f:
return f.read()
The code executes the ADB binary again using a path that may come from environment variables or command-line input. In an agent setting, this is particularly risky because a caller who can shape execution context may turn a mobile-automation helper into a local code-execution vector.
def dump_ui():
try:
adb_cmd("shell", "uiautomator", "dump", "/sdcard/ui_dump.xml")
subprocess.run([ADB, "pull", "/sdcard/ui_dump.xml", "/tmp/ui_dump.xml"],
capture_output=True, text=True, timeout=10)
with open("/tmp/ui_dump.xml") as f:
return f.read()
The script stores screenshots of profiles and detailed action logs locally, including summaries and comments tied to individuals. Because the content involves dating profiles, this creates a meaningful privacy and confidentiality risk if the host system is shared, compromised, or backed up insecurely.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
json.dump(payload, tmp)
tmp_path = tmp.name
result = subprocess.run(
["curl", "-s", "-X", "POST",
f"{GEMINI_URL}?key={GEMINI_API_KEY}",
"-H", "Content-Type: application/json",
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
json.dump(payload, tmp)
tmp_path = tmp.name
result = subprocess.run(
["curl", "-s", "-X", "POST",
f"{GEMINI_URL}?key={GEMINI_API_KEY}",
"-H", "Content-Type: application/json",
LOG_DIR is derived from HINGE_WORK_DIR, which comes from the environment, and is used for file writes without confinement checks. An attacker controlling that environment variable could redirect sensitive logs into arbitrary writable locations, causing unintended file overwrite or privacy exposure.
# Save JSON log
log_file = LOG_DIR / f"{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.json"
with open(log_file, "w") as f:
json.dump(summary, f, indent=2)
log(f"Log saved: {log_file}")
No suspicious patterns detected.