Back to skill

Security audit

iResponder

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its iMessage auto-responder purpose, but it combines private message access, automatic sending, external AI processing, plaintext logs, cross-skill credential reuse, and an unsafe Telegram command path.

Install only if you are comfortable granting Terminal Full Disk Access, letting the tool read your Messages history, sending selected conversation content to external AI providers, and allowing automatic replies from your account. Treat the Telegram management interface as sensitive admin access; avoid using it until the shell command injection and plaintext logging issues are fixed.

Vulnerability Patterns
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/telegram-handler.js:98
Finding
Shell Command Injection Through Telegram Management Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/telegram-handler.js:98-105, 108-116, 132-138, 358-412, 477-482` **Vulnerability Type**: OS command injection through unsafe shell interpolation **Risk Level**: Critical ### Vulnerable Code ```javascript function handleAdd(identifier, name, prompt) { execSync(`node "${MANAGE_SCRIPT}" add "${identifier}" "${prompt}" "${name}"`, { stdio: 'inherit' }); return `✅ Added **${name}** (\`${identifier}\`) to watch list.\n\nRestart watcher to apply changes.`; } function handleRemove(identifier) { const config = loadConfig(); const contact = config.watchList.find(c => c.identifier === identifier); const name = contact ? contact.name : identifier; execSync(`node "${MANAGE_SCRIPT}" remove "${identifier}"`, { stdio: 'inherit' }); return `✅ Removed **${name}** (\`${identifier}\`) from watch list.\n\nRestart watcher to apply changes.`; } function handleDelay(identifier, minutes) { execSync(`node "${MANAGE_SCRIPT}" set-delay "${identifier}" ${minutes}`, { stdio: 'inherit' }); ``` Additional affected handlers use the same construction: ```javascript function handleSetTimeWindow(identifier, start, end) { try { execSync(`node "${MANAGE_SCRIPT}" set-time-window "${identifier}" "${start}" "${end}"`, { encoding: 'utf8' }); ``` ```javascript function handleAddKeyword(identifier, keyword) { try { execSync(`node "${MANAGE_SCRIPT}" add-keyword "${identifier}" "${keyword}"`, { encoding: 'utf8' }); ``` ```javascript function handleRemoveKeyword(identifier, keyword) { try { execSync(`node "${MANAGE_SCRIPT}" remove-keyword "${identifier}" "${keyword}"`, { encoding: 'utf8' }); ``` ```javascript function handleSetDailyCap(identifier, maxReplies) { try { execSync(`node "${MANAGE_SCRIPT}" set-daily-cap "${identifier}" ${maxReplies}`, { encoding: 'utf8' }); ``` The arguments originate directly from the command line: ```javascript const [,, command, ...args] = proces ...[truncated 2767 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate all shell-string invocation. Use `execFileSync()` or `spawnSync()` with an argument array: ```javascript const { execFileSync } = require('child_process'); execFileSync(process.execPath, [ MANAGE_SCRIPT, 'add', identifier, prompt, name ], { stdio: 'inherit', shell: false }); ``` 2. Apply this change to every affected management handler, including add, remove, delay, time-window, keyword, bulk-delay, and daily-cap operations. 3. Prefer importing management functions as a local module rather than launching another process. 4. Validate contact identifiers against an explicit E.164 policy, such as `^\+[1-9]\d{1,14}$`, before use. 5. Validate numeric inputs with `Number.isInteger()` and enforce safe ranges rather than passing `parseInt()` results unchecked. 6. Restrict time values to the documented `HH:MM` format before process invocation. 7. Limit names, prompts, and keywords by length and reject control characters and null bytes. Validation is defense in depth and must not replace shell-free execution. 8. Ensure the Telegram integration authenticates an explicit owner or allowlist before exposing management commands. 9. After remediation, test all handlers with quotes, backticks, `$()`, semicolons, newlines, and leading option-like values to verify they remain literal arguments. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/watcher.js:132
Finding
Private iMessage History Is Uploaded to External AI Providers Without Adequate Privacy Disclosure or Data Minimization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/watcher.js:132-205`; duplicated in `scripts/watcher-old.js:91-174` **Vulnerability Type**: Sensitive information disclosure to third-party AI APIs **Risk Level**: High ### Vulnerable Code ```javascript async function generateResponse(contact, incomingMessage, messageHistory) { const historyText = messageHistory.slice(0, 5) .map(m => `${m.is_from_me ? 'Me' : contact.name || contact.identifier}: ${m.text || '[attachment]'}`) .join('\n'); const prompt = `${contact.prompt} Context - Recent message history (newest first): ${historyText} Latest message from ${contact.name || contact.identifier}: ${incomingMessage.text || '[attachment]'} Generate a response now:`; // Get OpenAI API key from Clawdbot config const configPath = path.join(os.homedir(), '.clawdbot', 'clawdbot.json'); let apiKey = process.env.OPENAI_API_KEY; if (!apiKey && fs.existsSync(configPath)) { try { const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); apiKey = config.skills?.['openai-whisper-api']?.apiKey || config.skills?.['openai-image-gen']?.apiKey; } catch (e) { // Ignore config parse errors } } if (!apiKey) { throw new Error('OPENAI_API_KEY not found'); } const payload = JSON.stringify({ model: 'gpt-4', messages: [{ role: 'user', content: prompt }], max_tokens: 150, temperature: 0.9 }); return new Promise((resolve, reject) => { const proc = spawn('curl', [ '-s', 'https://api.openai.com/v1/chat/completions', '-H', 'Content-Type: application/json', '-H', `Authorization: Bearer ${apiKey}`, '-d', payload ]); ``` The legacy watcher performs equivalent transmission to another provider: ```javascript const proc = spawn('curl', [ '-s', 'https://api.anthropic.com/v1/messages', '-H', 'Content-Type: application/json', '-H', `x-api-key: ${apiKey}`, '-H', 'anthropic-version: 2023-06-01', '-d ...[truncated 2522 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Present an explicit first-run disclosure identifying: - The external provider and endpoint. - Exactly which message fields are transmitted. - How many history entries are included. - Relevant provider retention and account-control considerations. 2. Require affirmative user opt-in before any conversation content is uploaded. 3. Make history inclusion configurable per contact, with the safest default being only the latest incoming message. 4. Add redaction for likely secrets, authentication codes, payment data, email addresses, phone numbers, and other configured sensitive patterns. 5. Provide a local-model option for users who cannot transmit private conversations to a third party. 6. Remove `watcher-old.js` from the production package, or clearly disable and document it so users cannot accidentally transmit data to an unexpected provider. 7. Separate system instructions, contact policy, conversation history, and latest input into appropriately scoped model messages. Explicitly identify conversation text as untrusted data. 8. Add a confirmation mode in which model output must be approved before `imsg send` is invoked, particularly when the incoming message appears to contain instructions or sensitive content. 9. Document which API credential is used. Do not silently reuse credentials belonging to unrelated skills such as image generation or transcription. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/watcher.js:70
Finding
Incoming Messages and AI-Generated Replies Are Persisted in Plaintext Logs and Exposed Through Management Output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/watcher.js:70-76, 331-355`; management exposure in `scripts/telegram-handler.js:158-211` **Vulnerability Type**: Plaintext storage and secondary disclosure of sensitive communications **Risk Level**: High ### Vulnerable Code ```javascript function log(message) { const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] ${message}\n`; // Only append to file, don't console.log (launcher already redirects stdout) fs.appendFileSync(LOG_PATH, logMessage); } ``` Message bodies and generated responses are logged directly: ```javascript log(`New message from ${contact.name || contact.identifier}: ${latestMessage.text || '[attachment]'}`); // Check rate limiting and conditions if (!shouldRespond(contact, state, config, latestMessage.text)) { state.processing[contact.identifier] = false; saveState(state); continue; } // Generate and send response try { log(`Generating response for ${contact.name || contact.identifier}...`); const response = await generateResponse(contact, latestMessage, messages); log(`Generated response: ${response}`); await sendMessage(contact.identifier, response); log(`✓ Sent response to ${contact.name || contact.identifier}`); ``` The Telegram handler returns recent raw log entries: ```javascript if (watcherStatus.running) { output += `🟢 **Watcher is RUNNING** (PID ${watcherStatus.pid})\n\n`; // Get recent log entries if (fs.existsSync(LOG_PATH)) { const logs = execSync(`tail -10 "${LOG_PATH}"`, { encoding: 'utf8' }); output += `**Recent Activity:**\n\`\`\`\n${logs}\`\`\``; } } ``` It also parses and returns generated responses: ```javascript const logs = fs.readFileSync(LOG_PATH, 'utf8'); const lines = logs.split('\n').filter(line => line.includes(identifier) || line.includes(name) ).slice(-limit * 3); const responses = []; for (let i = 0; i < lines.length; i++) { if (lines[i].includes('Generated response:' ...[truncated 2263 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not log message bodies or complete generated replies by default. Log only event type, timestamp, a non-reversible contact reference, and success or failure status. 2. Add an explicit opt-in debug mode for content logging and prominently warn that it records private communications. 3. Create log, state, and configuration files with restrictive permissions: ```javascript fs.appendFileSync(LOG_PATH, sanitizedLogMessage, { mode: 0o600 }); fs.chmodSync(LOG_PATH, 0o600); ``` 4. Ensure parent data and log directories are restricted to the current user, preferably mode `0700`. 5. Implement rotation based on age and size, with a short default retention period. 6. Redact likely secrets and personally identifiable information from diagnostic output. 7. Do not return raw log tails through Telegram status. Return structured, sanitized operational events. 8. Protect all Telegram management commands with an explicit owner allowlist and avoid relying solely on the surrounding agent to enforce authorization. 9. Remove or sanitize the logging behavior in `watcher-old.js` as well. 10. Document where logs are stored, what they contain, and how users can securely delete them. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/launcher.sh:25
Finding
PID File Is Trusted Without Verifying Process Identity<![CDATA[ ## Vulnerability Details **File Location**: `scripts/launcher.sh:25-42` **Vulnerability Type**: Unsafe process control using an untrusted or stale PID file **Risk Level**: Medium ### Vulnerable Code ```bash stop) if [ ! -f "$PID_FILE" ]; then echo "✗ Auto-responder is not running (no PID file)" exit 1 fi PID=$(cat "$PID_FILE") if ! kill -0 "$PID" 2>/dev/null; then echo "✗ Auto-responder is not running (stale PID file)" rm "$PID_FILE" exit 1 fi echo "Stopping iMessage auto-responder (PID $PID)..." kill "$PID" rm "$PID_FILE" echo "✓ Auto-responder stopped" ;; ``` ### Technical Analysis The launcher verifies only that some process currently exists with the numeric PID recorded in the file. It does not verify that the process is the expected `watcher.js` instance. PIDs are reused by the operating system. If the watcher exits without deleting its PID file and the number is later assigned to another process, invoking stop or restart can signal that unrelated process. The same issue occurs if another process able to modify the PID file substitutes an arbitrary PID. This background launcher is reasonable for the declared long-running watcher and does not install a startup service or cross-session scheduled task. Therefore, it is not confirmed system persistence. The defect is limited to unsafe lifecycle management. ### Attack Path 1. The watcher terminates unexpectedly while leaving its PID file behind, or a local actor modifies the PID file. 2. The operating system assigns that PID to another process, or the file is changed to refer to a chosen same-user process. 3. The user or Telegram management path invokes `launcher.sh stop` or `launcher.sh restart`. 4. `kill -0` succeeds because a process with that number exists. 5. The launcher sends `SIGTERM` to the unrelated process. ### Impact Assessment The command can terminate another process accessible to the current user, causing loss of unsaved work, servi ...[truncated 221 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Before sending a signal, verify that the PID belongs to the expected executable and script path. 2. Prefer a process supervisor such as `launchd` with a dedicated service label rather than implementing PID-file lifecycle management manually. 3. Store the PID file in a user-private directory with restrictive permissions. 4. Validate that the PID file contains only a positive integer before passing it to `kill`. 5. Use an atomic PID-file creation strategy and remove the file through process exit handlers. 6. On restart, treat failure to stop a missing or stale process as recoverable rather than blindly continuing with an ambiguous state. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (32)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documented behavior includes sending incoming messages and recent conversation history to an external AI provider and reading credentials from local secret storage, but those behaviors are not fully disclosed in the declared purpose/manifest. This is dangerous because users may enable an iMessage auto-responder without realizing private SMS/iMessage content is being exfiltrated to a third party and that the skill accesses local credentials and persistent files.

Ae1

High
Category
analysis-evasion
Content
The agent will understand and execute the command using the `telegram-handler.js` script.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
The agent will understand and execute the command using the `telegram-handler.js` script.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code falls back to reading API keys from unrelated Clawdbot skill entries (`openai-whisper-api` and `openai-image-gen`) instead of using a dedicated secret for this skill. This violates secret isolation and allows this component to appropriate credentials provisioned for other skills, increasing blast radius and enabling unintended cross-skill access to paid APIs or sensitive integrations.

Ssd 3

High
Confidence
99% confidence
Finding
The prompt includes the latest incoming message plus recent chat history, meaning the tool systematically exports private conversation data to an external AI service. Given this skill's messaging context, the data may include sensitive personal, financial, medical, or relationship content, making confidentiality loss a significant risk.

Missing User Warnings

High
Confidence
99% confidence
Finding
Incoming message content and conversation context are sent to Anthropic automatically, with no explicit per-contact consent flow, user confirmation, or clear runtime notice. In the context of an iMessage/SMS auto-responder, this is especially dangerous because highly sensitive personal communications may be exfiltrated to a third-party AI provider without the sender's or operator's informed awareness.

Missing User Warnings

High
Confidence
94% confidence
Finding
The code automatically sends generated messages without human review or confirmation. While auto-response is part of the feature's purpose, the absence of guardrails increases the risk of unintended, harmful, or policy-violating messages being sent to real contacts, especially when responses are generated from an external model.

Missing User Warnings

High
Confidence
98% confidence
Finding
This code sends recent message history and the latest inbound message to the OpenAI API, but the file contains no consent, disclosure, or privacy gating before exfiltrating private conversation data. In the context of an iMessage/SMS auto-responder, that is especially sensitive because users may process personal, financial, medical, or confidential communications without realizing they are being transmitted externally.

Ssd 3

High
Confidence
99% confidence
Finding
The code constructs a plain-language prompt containing message history and contact content, then forwards it to an external AI provider. Because the transmitted data is raw conversational text, any sensitive details present in chats are directly exposed to a third party, increasing privacy, compliance, and data handling risk.

Missing User Warnings

High
Confidence
90% confidence
Finding
The skill automatically sends iMessage replies without per-message confirmation or an explicit safety interlock in code. This can cause unintended communications, social engineering amplification, reputational harm, or accidental disclosure if prompts or context generate incorrect, manipulative, or confidential responses.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill is explicitly designed to send automatic replies on the user's behalf, yet the documentation does not foreground the risk of unintended, misleading, or contextually inappropriate messages being sent without per-message review. Because the system operates continuously and responds within seconds, mistakes can propagate quickly and affect personal, professional, or sensitive conversations.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README states that the AI uses recent conversation history for replies, but it does not prominently disclose that third-party message content may be transmitted to OpenAI for processing. In a messaging auto-responder, this creates a real privacy and consent risk because contacts may not expect their messages to be shared with an external AI provider.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill clearly instructs the agent/user to run shell commands, read environment/configured secrets, and operate with local system access, yet it declares no explicit tool scope or allowed-tools. That omission weakens security boundaries and informed consent because a caller cannot tell from the manifest that the skill needs shell and environment access to monitor Messages, read keys, and manage files.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill introduces AI-generated auto-replies without an upfront, explicit privacy warning that recent conversation history and incoming messages are transmitted to an external AI service. Because the content involved is iMessage/SMS data, delayed or buried disclosure materially increases the risk of unintentional privacy violations and non-consensual third-party sharing.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The documentation inconsistently references OpenAI and Anthropic/API key sources for the same functionality, which can mislead operators about where sensitive data is sent and which credentials are consumed. In a messaging auto-responder handling private conversations, provider ambiguity undermines privacy review, safe deployment, and incident response.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
Telegram-based management is described as recommended, but there is no clear warning that commands, contact identifiers, prompts, status/history, and possibly message-related content may be exposed through Telegram. Using a third-party chat platform to administer a private SMS/iMessage responder creates an additional data-leak surface that users should be clearly warned about.

Session Persistence

Medium
Category
Rogue Agent
Content
fi
    
    echo "Starting iMessage auto-responder..."
    nohup node "$WATCHER_SCRIPT" >> "$LOG_FILE" 2>&1 &
    echo $! > "$PID_FILE"
    echo "✓ Auto-responder started (PID $!)"
    echo "  Logs: $LOG_FILE"
Confidence
65% confidence
Finding
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.

Session Persistence

Medium
Category
Rogue Agent
Content
fi
    
    echo "Starting iMessage auto-responder..."
    nohup node "$WATCHER_SCRIPT" >> "$LOG_FILE" 2>&1 &
    echo $! > "$PID_FILE"
    echo "✓ Auto-responder started (PID $!)"
    echo "  Logs: $LOG_FILE"
Confidence
65% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The `remove` function deletes a watch-list entry and saves the updated configuration right away. This is an irreversible configuration change for the user, but the code provides no pre-action confirmation or warning beyond a success message after the deletion has already happened.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
Both `clearTimeWindows` and `clearKeywords` wipe all configured values for a contact and then persist the change immediately. These are destructive configuration resets, but there is no confirmation prompt or advance warning to the user before the data is cleared.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The `enable-all` and `disable-all` commands modify the autoresponder state for every configured contact and immediately persist the changes. Although the script logs after completion, there is no user-facing warning or confirmation before this broad, potentially disruptive operation occurs.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest describes a skill for monitoring iMessage/SMS conversations and managing auto-responder settings, but this file explicitly implements a Telegram command handler and exposes watcher restart/status process management capabilities. Those are broader control-surface and integration behaviors not mentioned in the skill description, so the code does more than the manifest claims at a semantic level.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
Test mode constructs a prompt containing a contact's name/identifier and the provided message, then transmits that content to OpenAI for response generation. In a messaging auto-responder context, this is privacy-sensitive conversational data, and the file provides no consent flow, redaction, or prominent disclosure before sending potentially personal communications to a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
return new Promise((resolve, reject) => {
    const proc = spawn('curl', [
      '-s',
      'https://api.openai.com/v1/chat/completions',
      '-H', 'Content-Type: application/json',
      '-H', `Authorization: Bearer ${apiKey}`,
      '-d', payload
Confidence
95% confidence
Finding
The script makes an outbound request to `https://api.openai.com/v1/chat/completions`, transmitting prompt content that includes messaging context and identifiers. External transmission is expected for AI-backed features, but in this skill context it is still security-relevant because it sends potentially sensitive communications to a third party and depends on proper disclosure, minimization, and consent.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The watcher reads an Anthropic API key from a separate global ~/.clawdbot configuration file, creating cross-tool credential access that exceeds the narrow scope of an iMessage auto-responder. That broadens the blast radius: anyone enabling this skill may unknowingly allow it to consume credentials provisioned for another application and send private message data to an external service.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/telegram-handler.js:103

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/watcher-old.js:65

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/watcher.js:81

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/telegram-handler.js:244

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/watcher-old.js:115