Back to skill

Security audit

iMessage & Signal Analyzer

Security checks for vulnerabilities and agentic risk

Overview

This skill is not obviously malicious, but it handles private message histories with broad permissions and weak safeguards that could expose more conversations than intended.

Review this carefully before installing. Only run it on message histories you have clear permission to analyze, prefer user-exported data over broad system access, avoid granting Full Disk Access to a general terminal or Python interpreter unless absolutely necessary, and revoke it immediately afterward. Use exact phone numbers or handles, avoid broad searches, and assume terminal output may contain private messages and contact details.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:16
Finding
Overbroad Full Disk Access Recommendation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:16-21` **Vulnerability Type**: Excessive operating-system permissions **Risk Level**: Medium ### Vulnerable Content ```markdown **Option 2:** If you get a permission error, grant Full Disk Access: - Open **System Settings → Privacy & Security → Full Disk Access** - Click **+** and add Python or your terminal app ``` ### Technical Analysis The documentation recommends granting macOS Full Disk Access to a general-purpose Python interpreter or terminal application. This permission is substantially broader than the read access needed for the single iMessage database at `~/Library/Messages/chat.db`. Full Disk Access is assigned to the application rather than narrowly to this script or one database. Consequently, unrelated programs subsequently launched through the authorized terminal or interpreter may inherit access to protected user data. The script does not itself exploit the granted permission or access unrelated protected files. The vulnerability is the documented privilege model, which violates least privilege and expands the consequences of any later malicious or compromised code executed through the authorized application. ### Attack Path 1. The user runs the analyzer and encounters a permission error. 2. Following `SKILL.md`, the user grants Full Disk Access to Python or the terminal. 3. The broad permission remains enabled after analysis finishes. 4. The user later executes an unrelated, malicious, or compromised script through that authorized application. 5. That process accesses protected files beyond the iMessage database under the inherited authorization. ### Impact Assessment An attacker able to execute code through the authorized Python interpreter or terminal could potentially read macOS-protected information available to that application, including messages and other private application data. The precise scope depends on macOS controls and the authorized application. This iss ...[truncated 135 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not recommend persistent Full Disk Access for a general-purpose terminal or Python interpreter. - Prefer having the user export or copy only the required conversation data into a dedicated, user-approved location. - If elevated access is unavoidable, use a narrowly scoped, signed helper rather than a general interpreter. - Explain the permission's full scope before requesting it. - Instruct users to revoke temporary authorization immediately after completing the analysis. - Ensure the analyzer requests explicit confirmation before accessing message content. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyze.py:27
Finding
Ambiguous iMessage Handle Matching Can Expose Unrelated Conversations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:27-34`, with downstream use at `scripts/analyze.py:49-69` and input handling at `scripts/analyze.py:218` **Vulnerability Type**: Overbroad sensitive-data selection **Risk Level**: Medium ### Vulnerable Code ```python query = """ SELECT DISTINCT id FROM handle WHERE id LIKE ? OR id = ? """ search_pattern = f"%{phone_or_handle}%" c.execute(query, (search_pattern, phone_or_handle)) handles = [row['id'] for row in c.fetchall()] ``` The resulting handle set is used to retrieve messages: ```python placeholders = ','.join(['?' for _ in handles]) query = f""" SELECT message.ROWID, message.text, message.date, message.is_from_me, message.attributedBody FROM message JOIN chat_message_join cmj ON message.ROWID = cmj.message_id JOIN chat_handle_join chj ON cmj.chat_id = chj.chat_id JOIN handle ON chj.handle_id = handle.ROWID WHERE handle.id IN ({placeholders}) ORDER BY message.date DESC """ if limit: query += f" LIMIT {limit}" c.execute(query, handles) ``` Interactive input can be empty: ```python phone = sys.argv[2] if len(sys.argv) > 2 else input("Phone/Handle: ").strip() ``` ### Technical Analysis The SQL statements use parameter binding, so this is not SQL injection. The security issue is the matching policy: arbitrary input is surrounded by `%` wildcards without minimum-length, format, or empty-value validation. An empty value produces `LIKE '%%'`, which matches every handle. A short or partial phone number, email fragment, or common domain can also match multiple unrelated handles. The script then queries chats associated with all matched handles and prints recent message text. Although the script reports the number and names of matching handles, it does not require confirmation when the ...[truncated 974 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject empty or whitespace-only identifiers before querying the database. - Require exact normalized phone-number or email-address matching by default. - Validate phone numbers and require a reasonable minimum length for any explicitly enabled partial search. - Escape SQL `LIKE` metacharacters if partial matching remains available. - If multiple handles match, display masked candidates and require explicit user selection before reading messages. - Add an explicit confirmation step describing the number of chats and messages that will be processed. - Fail closed when the identifier is ambiguous rather than automatically aggregating all matches. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyze.py:49
Finding
Group-Chat Messages Are Exposed and Misattributed to the Selected Contact<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:49-69`, with attribution logic at `scripts/analyze.py:142-144` and `scripts/analyze.py:200-211` **Vulnerability Type**: Improper authorization scope and sender attribution **Risk Level**: Medium ### Vulnerable Code ```python placeholders = ','.join(['?' for _ in handles]) query = f""" SELECT message.ROWID, message.text, message.date, message.is_from_me, message.attributedBody FROM message JOIN chat_message_join cmj ON message.ROWID = cmj.message_id JOIN chat_handle_join chj ON cmj.chat_id = chj.chat_id JOIN handle ON chj.handle_id = handle.ROWID WHERE handle.id IN ({placeholders}) ORDER BY message.date DESC """ if limit: query += f" LIMIT {limit}" c.execute(query, handles) ``` All non-local senders are treated as the selected contact: ```python total = len(messages) from_me = sum(1 for m in messages if m['is_from_me']) from_them = total - from_me ``` ```python for m in messages[:10]: text = (m['text'] or '')[:100] + "..." if len(m['text'] or '') > 100 else (m['text'] or '') sender = "You" if m['is_from_me'] else "Them" try: if source == "iMessage": ts = datetime(2001, 1, 1) + timedelta(seconds=m['date'] or 0) else: ts = datetime.fromtimestamp(m['date']) ts_str = ts.strftime('%Y-%m-%d') except: ts_str = "?" print(f" [{ts_str}] {sender}: {text}") ``` ### Technical Analysis The query finds every chat containing the selected handle and then selects every message in each matching chat. It does not restrict results to one-to-one conversations and does not retrieve the actual sender handle for each message. If the selected contact participates in a group chat, messages written by every other participant in ...[truncated 1194 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict default analysis to verified one-to-one chats. - Determine chat membership before retrieving message content. - For group chats, require explicit user consent and clearly identify all included participants. - Join each message to its actual sender handle and preserve that identity throughout analysis. - Never collapse every non-local participant into a single “Them” category. - Exclude third-party messages when the requested analysis concerns one specific relationship. - Add tests covering one-to-one chats, group chats, duplicate handles, and mixed SMS/iMessage conversations. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/analyze.py:96
Finding
Signal No-Match Error Enumerates Unrelated Contact Details<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:96-101` **Vulnerability Type**: Unnecessary disclosure of contact information **Risk Level**: Low ### Vulnerable Code ```python if not recipient_id: print(f"No recipient found matching: {phone_or_name}") print("\nAvailable recipients:") for r in data.get('recipients', [])[:10]: print(f" - {r.get('name', 'Unknown')} ({r.get('number', 'no number')})") return ``` ### Technical Analysis When a Signal recipient does not match, the script automatically prints up to ten unrelated recipient names and phone numbers from the export. These contacts are outside the requested conversation-analysis scope. The output may be captured in terminal scrollback, shell-session recording, CI logs, Agent transcripts, or other logging systems. A failed lookup therefore causes unnecessary disclosure rather than returning a minimal error. ### Attack Path 1. The analyzer receives a nonexistent or deliberately nonmatching recipient identifier. 2. The recipient search fails. 3. The failure branch iterates over the first ten entries in the Signal recipient list. 4. Names and phone numbers are printed to the output stream. 5. Any system recording that output retains contact details unrelated to the requested analysis. ### Impact Assessment Up to ten unrelated contact names and phone numbers can be disclosed per invocation. The actual contacts exposed depend on export ordering. The flaw does not expose message bodies through this specific branch and does not provide code execution or elevated privileges. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Return only a generic no-match error by default. - Do not enumerate contacts as an implicit side effect of a failed lookup. - If contact listing is necessary, place it behind a separate explicit option such as `--list-contacts`. - Require user confirmation before listing contacts. - Mask phone numbers and limit displayed identifying information. - Warn users that command output may be retained in terminal or Agent logs. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Missing User Warnings

High
Confidence
96% confidence
Finding
The documentation tells users how to grant Full Disk Access to Python or a terminal app in order to reach `~/Library/Messages/chat.db`, but it does not prominently warn that this enables reading extremely sensitive local communications and potentially broader filesystem access by that app. Because the skill is specifically designed to inspect private conversations and infer relationship dynamics, this omission materially increases privacy and abuse risk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill can read highly sensitive local data sources (iMessage chat history, AddressBook database, Signal exports) but does not declare any explicit tool scope or permission boundaries. That makes the effective data-access surface opaque and increases the chance the skill is invoked without clear user understanding that private message history and contacts may be accessed.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The description uses broad trigger phrases like analyzing messages, reading message history, checking conversation patterns, or evaluating a relationship, which can match common conversational requests and cause overbroad auto-invocation. In this skill's context, accidental invocation is more dangerous because it targets intimate communications and relationship inferences from sensitive personal data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script accesses a highly sensitive local iMessage database and prints matching handles and recent message content to stdout without any privacy notice, confirmation step, or output minimization. In a skill explicitly designed to analyze private relationship communications, this increases the risk of inadvertent disclosure through terminal logs, agent transcripts, screenshots, or shared sessions.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The Signal path loads exported conversation history from JSON and prints recipient details and recent message contents with no user-facing warning or consent checkpoint. Because Signal exports can contain sensitive plaintext archives, this behavior can easily leak private communications into console history, logs, or agent-visible outputs in a context centered on intimate relationship analysis.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The advertised purpose includes revealing tone samples and evaluating relationship dynamics based on text history. The actual analysis computes message counts, yearly volume, crude initiation estimates, silence gaps, and shows recent message excerpts; it does not perform sentiment, tone, or relationship-quality analysis beyond simple heuristics.

Description-Behavior Mismatch

Low
Confidence
89% confidence
Finding
The manifest says the skill analyzes Signal conversation history on Linux/Windows and macOS, which implies direct analysis of existing message history. In this file, Signal support only reads a user-supplied export file and even instructs the user to use signal-cli to create it, which is a narrower behavior than the description suggests.

Static analysis

No suspicious patterns detected.