Back to skill

Security audit

Video Chat With Me

Security checks for vulnerabilities and agentic risk

Overview

This is a real video-chat skill, but it needs review because it exposes an unauthenticated network bridge to the user's main OpenClaw agent and installs persistent background service behavior.

Review before installing. Use it only if you are comfortable sending audio to Groq, TTS text to Microsoft, and camera frames/text through your OpenClaw gateway to the configured model. Before use, restrict the server to localhost or add authentication, avoid exposing it on LAN/Tailscale without pairing, remove or explicitly opt into launchd persistence, store the Groq key with restrictive permissions or a keychain, and prefer an isolated least-privileged OpenClaw agent instead of the main agent.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
Findings (5)

T06 · System Persistence

Error
Location
scripts/setup.sh:155
Finding
Unconditional Persistent Launch Agent Installation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:155-214` **Vulnerability Type**: Unconditional cross-session service persistence **Risk Level**: High ### Vulnerable Code ```bash PLIST="$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist" launchctl stop "$PLIST_LABEL" 2>/dev/null || true launchctl unload "$PLIST" 2>/dev/null || true cat > "$PLIST" << PLISTEOF <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>${PLIST_LABEL}</string> <key>ProgramArguments</key> <array> <string>$(which python3)</string> <string>${SCRIPTS_DIR}/server.py</string> </array> <key>WorkingDirectory</key> <string>${SKILL_DIR}</string> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <dict> <key>SuccessfulExit</key> <false/> </dict> <key>ThrottleInterval</key> <integer>5</integer> <key>StandardOutPath</key> <string>/tmp/videochat-withme.log</string> <key>StandardErrorPath</key> <string>/tmp/videochat-withme.log</string> <key>EnvironmentVariables</key> <dict> <key>AGENT_NAME</key> <string>${AGENT_NAME}</string> <key>USER_NAME</key> <string>${USER_NAME}</string> <key>PORT</key> <string>${PORT}</string> <key>PATH</key> <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string> PLISTEOF if [ -n "$SSL_CERT" ] && [ -n "$SSL_KEY" ]; then cat >> "$PLIST" << SSLEOF <key>SSL_CERT</key> <string>${SSL_CERT}</string> <key>SSL_KEY</key> <string>${SSL_KEY}</string> SSLEOF fi cat >> "$PLIST" << ENDEOF </dict> </dict> </plist> ENDEOF launchctl load "$PLIST" ``` ### Technical Analysis The setup process always creates and loads a per-user launch agent. `RunAtLoad` causes the service to start whenever the user logs in, while ...[truncated 1628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not install a launch agent during default or automatic setup. 2. Add an explicit option such as `--install-service` and require informed user confirmation before using it. 3. Run the server in the foreground or start it only for the duration of a call by default. 4. Add a complete uninstall operation using the appropriate `launchctl bootout` or unload command, followed by deletion of the plist. 5. Make `stop.sh` distinguish between temporary stopping and permanent service removal. 6. Clearly document startup, restart, and removal behavior before installation. 7. Consider configuring `RunAtLoad` and `KeepAlive` as disabled unless the user expressly requests continuous availability. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/server.py:101
Finding
Unauthenticated Network Bridge to the Privileged Main OpenClaw Agent<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:101-158, 228-247, 425-438` **Vulnerability Type**: Missing authentication and excessive agent privilege **Risk Level**: Critical ### Vulnerable Code ```python @app.post("/api/chat") async def chat( audio: UploadFile = File(...), image: Optional[UploadFile] = File(None), speed: str = Form("normal"), language: str = Form("zh"), voice: str = Form("zh-CN-XiaoxiaoNeural"), ): req_id = uuid.uuid4().hex # Save uploads audio_path = AUDIO_DIR / f"{req_id}.webm" audio_bytes = await audio.read() audio_path.write_bytes(audio_bytes) image_bytes = None if image is not None: image_bytes = await image.read() if image_bytes: image_path = AUDIO_DIR / f"{req_id}.jpg" image_path.write_bytes(image_bytes) # Convert audio for Whisper wav_path = AUDIO_DIR / f"{req_id}.wav" try: result = subprocess.run( ["ffmpeg", "-y", "-i", str(audio_path), "-ar", "16000", "-ac", "1", str(wav_path)], capture_output=True, timeout=30, ) if result.returncode != 0 or not wav_path.exists(): logger.warning("ffmpeg failed (rc=%d)", result.returncode) wav_path = audio_path except (FileNotFoundError, subprocess.TimeoutExpired) as e: logger.warning("ffmpeg exception: %s", e) wav_path = audio_path transcription = transcribe_audio(wav_path) img_b64 = base64.b64encode(image_bytes).decode("utf-8") if image_bytes else None reply_text = await get_openclaw_reply(transcription, img_b64, language) reply_filename = f"{req_id}_reply.mp3" reply_path = AUDIO_DIR / reply_filename generate_tts(reply_text, reply_path, voice=voice, speed=speed) return { "text": reply_text, "audio_url": f"/audio/{reply_filename}", "transcription": transcription, } ``` ```python headers = { "Authorization": f"Bearer { ...[truncated 3119 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind to `127.0.0.1` by default and require explicit configuration before accepting remote connections. 2. Generate a cryptographically strong, per-installation access token and require it on every API route. 3. Use short-lived call-session tokens rather than a single indefinitely reusable credential. 4. Implement an explicit pairing or approval flow for mobile and Tailscale clients. 5. Enforce rate limits, request quotas, maximum concurrent operations, and temporary lockouts. 6. Validate `Origin` and `Host` headers and add appropriate CSRF defenses. 7. Route video-chat requests to a dedicated least-privileged agent with no tools and no sensitive memory by default. 8. Do not route arbitrary network clients directly to the `main` agent. 9. Restrict network access with host firewall rules or Tailscale identity-based access controls. 10. Log authentication failures and security-relevant request metadata without logging private media or credentials. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server.py:101
Finding
Unbounded Sensitive Upload Storage Without Cleanup<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:26-27, 101-158` **Vulnerability Type**: Unsafe temporary-file handling and denial of service **Risk Level**: High ### Vulnerable Code ```python AUDIO_DIR = Path(tempfile.gettempdir()) / "videochat_me_audio" AUDIO_DIR.mkdir(exist_ok=True) ``` ```python @app.post("/api/chat") async def chat( audio: UploadFile = File(...), image: Optional[UploadFile] = File(None), speed: str = Form("normal"), language: str = Form("zh"), voice: str = Form("zh-CN-XiaoxiaoNeural"), ): req_id = uuid.uuid4().hex # Save uploads audio_path = AUDIO_DIR / f"{req_id}.webm" audio_bytes = await audio.read() audio_path.write_bytes(audio_bytes) image_bytes = None if image is not None: image_bytes = await image.read() if image_bytes: image_path = AUDIO_DIR / f"{req_id}.jpg" image_path.write_bytes(image_bytes) # Convert audio for Whisper wav_path = AUDIO_DIR / f"{req_id}.wav" try: result = subprocess.run( ["ffmpeg", "-y", "-i", str(audio_path), "-ar", "16000", "-ac", "1", str(wav_path)], capture_output=True, timeout=30, ) if result.returncode != 0 or not wav_path.exists(): logger.warning("ffmpeg failed (rc=%d)", result.returncode) wav_path = audio_path except (FileNotFoundError, subprocess.TimeoutExpired) as e: logger.warning("ffmpeg exception: %s", e) wav_path = audio_path transcription = transcribe_audio(wav_path) img_b64 = base64.b64encode(image_bytes).decode("utf-8") if image_bytes else None reply_text = await get_openclaw_reply(transcription, img_b64, language) # TTS with speed and voice reply_filename = f"{req_id}_reply.mp3" reply_path = AUDIO_DIR / reply_filename generate_tts(reply_text, reply_path, voice=voice, speed=speed) return { "text": reply_text, "audio_url": f"/audi ...[truncated 2159 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce strict request-body, audio, and image size limits before reading uploads. 2. Stream uploads to bounded files instead of loading entire inputs into memory. 3. Create a private temporary directory with permissions limited to the current user. 4. Track every generated path and delete it in a `finally` block on both success and failure. 5. Delete reply audio after one successful retrieval or after a short expiration interval. 6. Add a periodic cleanup task with conservative age and size thresholds. 7. Limit recording duration and validate media type before invoking `ffmpeg`. 8. Restrict concurrent transcoding and TTS operations. 9. Monitor available disk space and reject work before critically low thresholds are reached. 10. Correct the privacy documentation to accurately disclose any unavoidable temporary storage and its retention period. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup.sh:37
Finding
Unpinned Global Dependency Installation During Setup and Startup<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:37-57` **Vulnerability Type**: Unsafe and non-reproducible dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash if [ -n "$MISSING" ]; then if command -v brew >/dev/null 2>&1; then echo " Installing:$MISSING" brew install $MISSING else echo "❌ Missing:$MISSING" echo " Install with: brew install$MISSING" exit 1 fi fi echo " ✅ python3 $(python3 --version 2>&1 | awk '{print $2}')" echo " ✅ ffmpeg" # 2. Install Python deps echo "" echo "📦 Installing Python dependencies..." pip3 install --break-system-packages -q fastapi uvicorn python-multipart httpx edge-tts 2>/dev/null || \ pip3 install -q fastapi uvicorn python-multipart httpx edge-tts echo " ✅ Done" ``` The same unsafe Python installation behavior also occurs during ordinary startup in `scripts/start.sh:19-23`: ```bash pip3 install --break-system-packages -q fastapi uvicorn python-multipart httpx edge-tts 2>/dev/null || \ pip3 install -q fastapi uvicorn python-multipart httpx edge-tts exec python3 server.py ``` ### Technical Analysis The scripts install packages without pinned versions or integrity hashes. Dependency resolution therefore depends on mutable repository state at execution time, making the reviewed source insufficient to determine the exact code that will run. The `--break-system-packages` option deliberately bypasses protections intended to prevent global Python-environment modification. Moreover, `start.sh` performs package installation as part of ordinary startup, so a routine attempt to launch the service can unexpectedly modify the user's environment and execute package installation logic. The audit did not identify a confirmed malicious package or typosquatted dependency. The issue is the unsafe supply-chain and environment-management practice itself. ### Attack Path 1. A user executes `setup.sh` or invokes `start.sh` when no launchd service is runni ...[truncated 943 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create and use a dedicated virtual environment within a controlled Skill data directory. 2. Pin every direct and transitive Python dependency to reviewed versions. 3. Use a lock file and require package hashes, such as through `pip --require-hashes`. 4. Remove `--break-system-packages`. 5. Remove all installation operations from `start.sh`; startup should only validate and launch a previously prepared environment. 6. Separate environment provisioning from runtime and require explicit user approval for package-manager changes. 7. Pin or document tested Homebrew package versions where operationally practical. 8. Add automated dependency scanning and a controlled update process. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/call.sh:6
Finding
AppleScript Injection Through Environment-Controlled Agent Name<![CDATA[ ## Vulnerability Details **File Location**: `scripts/call.sh:6-12` **Vulnerability Type**: Script injection **Risk Level**: High ### Vulnerable Code ```bash PORT=${PORT:-8766} PROTO=${PROTO:-https} AGENT_NAME=${AGENT_NAME:-"AI Assistant"} # Pop up incoming call dialog RESULT=$(osascript -e " display dialog \"📞 ${AGENT_NAME} incoming call...\" buttons {\"Decline\", \"Accept\"} default button \"Accept\" with title \"Incoming Call\" with icon caution giving up after 30 " 2>&1) || true ``` ### Technical Analysis `AGENT_NAME` is inserted directly into AppleScript source enclosed by double quotes. Shell expansion occurs before `osascript` parses the script. An attacker who can influence this environment variable can include quotes, statement separators, and AppleScript syntax that terminate the intended string and introduce additional statements. This is a source-code injection flaw rather than ordinary display-string handling. Shell quoting around the multiline argument does not make the expanded value safe for the AppleScript parser. The setup script stores an agent name in the launch-agent environment, while `call.sh` independently accepts `AGENT_NAME` from its invocation environment. Exploitation therefore requires control over the environment or invocation context used for `call.sh`; the unauthenticated HTTP endpoint does not directly set this variable. ### Attack Path 1. An attacker gains the ability to influence the `AGENT_NAME` environment variable used when `call.sh` is invoked, such as through an unsafe wrapper, automation, or compromised calling context. 2. The attacker supplies a value containing a closing quote and additional AppleScript statements. 3. Shell expansion inserts that value into the string passed to `osascript -e`. 4. `osascript` parses the injected statements as executable AppleScript rather than dialog text. 5. Injected AppleScript can invoke facilities such as `do shell script`, executing commands under the account that ...[truncated 513 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pass the display name as an argument rather than concatenating it into AppleScript source. 2. Read the value from `argv` inside a fixed AppleScript program. For example: ```bash RESULT=$(osascript - "$AGENT_NAME" <<'APPLESCRIPT' on run argv set agentName to item 1 of argv display dialog ("📞 " & agentName & " incoming call...") buttons {"Decline", "Accept"} default button "Accept" with title "Incoming Call" with icon caution giving up after 30 end run APPLESCRIPT ) || true ``` 3. Apply a reasonable maximum length and reject control characters in display names. 4. Avoid implementing manual escaping when the target interpreter supports argument passing. 5. Review all configuration values that are inserted into shell, plist, HTML, or interpreter source for equivalent injection risks. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Rogue AgentSelf-Modification, Session Persistence
Findings (47)

Tainted flow: 'text' from httpx.post (line 247, network input) → subprocess.run (code execution)

Critical
Category
Data Flow
Content
for attempt in range(1, max_retries + 1):
        try:
            logger.info("TTS attempt %d/%d for %d chars", attempt, max_retries, len(text))
            result = subprocess.run(
                [
                    "edge-tts",
                    "--voice", voice,
Confidence
90% confidence
Finding
External input (network, user) flows to a code execution sink. This enables remote code execution or command injection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior presents a full real-time video chat pipeline, but the operational instructions mainly implement a launcher that checks a local service, runs setup, and opens a browser or notification flow. This mismatch is dangerous because users and reviewers may consent to one set of behaviors while the skill actually performs different local automation and setup actions, reducing informed consent and masking risk.

Exfiltration Commands

High
Category
Prompt Injection
Content
async def get_openclaw_reply(transcript: str, image_b64: str | None, language: str = "zh") -> str:
    """
    Send message to OpenClaw chatCompletions API.
    Routes to the main agent with full memory and personality.
    The 'user' field creates a stable session for videochat conversations.
    """
Confidence
90% confidence
Finding
This code forwards transcripts and optional camera frames into the main OpenClaw agent with persistent session identity and full memory/personality. In the stated skill context, that means highly sensitive real-time data can be propagated into a broader agent environment and possibly to a configured cloud LLM, increasing exfiltration and over-collection risk beyond a narrow video-chat function.

Missing User Warnings

High
Confidence
98% confidence
Finding
The script prompts for a Groq API key and writes it to ~/.openclaw/secrets/groq_api_key.txt without clearly disclosing credential-at-rest risks or setting restrictive permissions. Storing cloud credentials on disk can expose them to other local processes, backups, or accidental leakage, and this skill routes audio to a cloud STT provider so the credential has real external-service value.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill instructs the agent to perform shell commands, read and write files under ~/.openclaw, access secrets, and make network requests, yet it declares no explicit tool scope or permission boundaries. This creates an authorization gap where an agent may execute sensitive operations without clear least-privilege constraints or user-visible approval.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Trigger phrases like 'call me', 'voice call', and common Chinese conversational phrases are broad enough to match ordinary dialogue, increasing the chance of unintended activation. In this skill's context, accidental invocation is more serious because activation can lead to setup actions, network exposure, browser opening, or persistent service installation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The setup guidance says it 'handles everything' but does not prominently warn at the point of execution that running setup may install a persistent launchd service. Users may approve the command without understanding that it creates background persistence, which has meaningful security and privacy implications for a camera/microphone-related skill.

Session Persistence

Medium
Category
Rogue Agent
Content
1. Get a free key at: https://console.groq.com/keys
2. Save it:
   ```bash
   mkdir -p ~/.openclaw/secrets
   echo "your-key-here" > ~/.openclaw/secrets/groq_api_key.txt
   ```
   Or set env var: `export GROQ_API_KEY="your-key-here"`
Confidence
78% confidence
Finding
The instructions persist an API key to a plaintext file under ~/.openclaw/secrets, creating long-lived local credential storage that could be exposed through weak filesystem permissions, backups, or other local compromise. While common in developer tooling, it still increases the attack surface by retaining a reusable secret beyond the immediate session.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code captures camera frames with canvas.toBlob() and attaches the image to the /api/chat request whenever the camera is enabled, but there is no explicit warning at send time that snapshots are being transmitted to the backend and potentially onward to an LLM provider. Because camera imagery is highly sensitive and the skill metadata says frames may leave the machine, the lack of clear disclosure materially increases privacy risk.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The page records microphone input and automatically uploads it to /api/chat, but the UI only indicates recording state and does not clearly warn that captured speech will be transmitted off-device and may be retained or processed by backend/cloud services. In a voice/video chat skill, users may assume local processing, so the omission creates a real privacy/security issue through inadequate informed consent rather than a code-execution flaw.

Context Window Stuffing

Medium
Category
Memory Poisoning
Content
function unlockAudio() {
    if (audioUnlocked) return;
    audioUnlocked = true; // Set immediately to prevent re-entry
    const silentAudio = new Audio('data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA//tQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAABhgC7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7//////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjEzAAAAAAAAAAAAAAAAJAAAAAAAAAAAAYYoRBqpAAAAAAD/+1DEAAAHAAGf9AAAIiWJs/80YAAAABBBP/k/JCCBnn5QEDBh+sCDv/KAgb/5OD4Pg+D7wfB8HwfB////8uD4Pg+D4nB8HwfB8Hw==');
    silentAudio.play().then(() => {
      console.log('Audio unlocked');
    }).catch(() => {});
Confidence
80% confidence
Finding
Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

External Transmission

Medium
Category
Data Exfiltration
Content
GROQ_API_KEY = _load_groq_key()
GROQ_BASE = "https://api.groq.com/openai/v1"

GW_PORT, GW_TOKEN = _load_gateway_config()
OPENCLAW_BASE = f"http://127.0.0.1:{GW_PORT}"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The request defaults set `language` to `zh` and the default TTS voice to a Chinese voice, while transcription is also hard-coded with `language: "zh"`. This imposes a specific language/locale behavior by default rather than offering a neutral default or explicit opt-in.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The server stores uploaded microphone and camera data on disk, then sends audio to Groq and image/text to OpenClaw and potentially onward to a cloud LLM, but this file contains no consent gate, disclosure mechanism, retention policy, or deletion logic. In a voice/video skill, that materially increases privacy risk because highly sensitive biometric and contextual data can leave the machine and persist locally without clear user awareness.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Convert audio for Whisper
    wav_path = AUDIO_DIR / f"{req_id}.wav"
    try:
        result = subprocess.run(
            ["ffmpeg", "-y", "-i", str(audio_path), "-ar", "16000", "-ac", "1", str(wav_path)],
            capture_output=True, timeout=30,
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
Speech transcription is hard-coded to Chinese even though the request includes a language parameter. In this context, forced misclassification can lead to incorrect transcripts being sent to the downstream agent, potentially causing privacy-impacting misunderstandings, unsafe actions, or leakage of sensitive spoken content through mistranscription.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
for attempt in range(1, max_retries + 1):
        try:
            logger.info("TTS attempt %d/%d for %d chars", attempt, max_retries, len(text))
            result = subprocess.run(
                [
                    "edge-tts",
                    "--voice", voice,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"\n".join(f"file '{p}'" for p in part_paths),
                    encoding="utf-8",
                )
                result = subprocess.run(
                    [
                        "ffmpeg", "-y", "-f", "concat", "-safe", "0",
                        "-i", str(list_file), "-c", "copy", str(output_path),
Confidence
83% confidence
Finding
This ffmpeg concat flow writes a manifest file using unescaped paths inside single quotes. If a filename ever contains a quote or concat-metacharacters, ffmpeg may misparse the manifest and read unintended files; the current part filenames are derived from output_path, which is mostly internal, so exploitability is constrained but the pattern is still unsafe.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _write_silent_mp3(path: Path):
    """Generate a short silent MP3 file using ffmpeg."""
    try:
        subprocess.run(
            [
                "ffmpeg", "-y", "-f", "lavfi", "-i",
                "anullsrc=r=24000:cl=mono", "-t", "0.5",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
In auto mode, the script will install missing system packages with Homebrew without an explicit confirmation step. That behavior changes the host environment and executes package-manager actions on the user's machine, which is risky for a setup script and especially sensitive in a skill that is intended to be easily triggered and deployed.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script installs Python packages globally via pip3, including a fallback to --break-system-packages, without a strong warning or isolation. This can alter or damage the host Python environment, introduce dependency conflicts, and makes the script perform privileged-looking persistent environment changes beyond simple skill setup.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "   ⚠️  Groq API key not found."
    read -p "   Paste your Groq API key (or Enter to skip): " INPUT_KEY
    if [ -n "$INPUT_KEY" ]; then
      mkdir -p "$HOME/.openclaw/secrets"
      echo "$INPUT_KEY" > "$HOME/.openclaw/secrets/groq_api_key.txt"
      echo "   ✅ Saved"
    fi
Confidence
97% confidence
Finding
This is true session persistence of a sensitive secret: the script stores the provided API key in a predictable file under the user's home directory for reuse across sessions. While intended for convenience, persistent plaintext secret storage increases the blast radius of local compromise and credential theft.

Missing User Warnings

Medium
Confidence
99% confidence
Finding
The script installs and loads a launchd LaunchAgent, creating persistence at user login, without a prominent upfront warning or separate consent flow. Persistent background execution materially increases risk because this skill handles camera, microphone, and network communication, so users should explicitly approve always-on behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
done
fi

PLIST="$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"
launchctl stop "$PLIST_LABEL" 2>/dev/null || true
launchctl unload "$PLIST" 2>/dev/null || true
Confidence
97% confidence
Finding
Although the token matched is generic, the actual line points to a LaunchAgents plist location used for persistence. In this script's context, that path is not inert metadata; it is directly used to deploy a user-level persistent service.

Session Persistence

Medium
Category
Rogue Agent
Content
done
fi

PLIST="$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"
launchctl stop "$PLIST_LABEL" 2>/dev/null || true
launchctl unload "$PLIST" 2>/dev/null || true
Confidence
97% confidence
Finding
Although the token matched is generic, the actual line points to a LaunchAgents plist location used for persistence. In this script's context, that path is not inert metadata; it is directly used to deploy a user-level persistent service.

Static analysis

No suspicious patterns detected.