Back to skill

Security audit

WhatsApp HappyBDay

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned but needs Review because it can monitor all WhatsApp groups, retain private message text, run on a schedule, and automatically send messages while also containing a command-injection flaw.

Install only if you are comfortable granting a scheduled tool access to your WhatsApp groups and allowing it to post messages. Keep simulation mode enabled until you have reviewed behavior, avoid live sending without a group allowlist and explicit consent, and do not use the current script on sensitive accounts until the shell=True command execution and plaintext message retention are fixed.

Vulnerability Patterns
  • 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
  • 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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/whatsapp_happybday.py:115
Finding
Shell Command Injection Through Dynamically Constructed wacli Commands<![CDATA[ ## Vulnerability Details **File Location**: `scripts/whatsapp_happybday.py`, lines 115-119, 134-135, and 218-219 **Vulnerability Type**: OS command injection through `shell=True` **Risk Level**: High ### Vulnerable Code ```python def run_wacli_command(cmd): """Execute wacli command""" try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=60 ) return result.stdout, result.stderr, result.returncode except Exception as e: return "", str(e), 1 ``` ```python def get_recent_messages(group_jid, today): """Get recent messages from a group (Text fields only)""" cmd = f'wacli messages list --chat "{group_jid}" --after {today} --json' stdout, stderr, rc = run_wacli_command(cmd) ``` ```python def send_message(group_jid, msg): cmd = f'wacli send text --message "{msg}" --to "{group_jid}"' stdout, stderr, rc = run_wacli_command(cmd) if rc != 0 or not stdout.strip(): return False return True ``` ### Technical Analysis The program interpolates `group_jid` and `msg` into command strings and executes those strings through a command shell. Quoting the interpolated values with double quotes does not make this safe: embedded quotation marks, command substitutions, and other shell metacharacters can terminate or alter the intended argument. The generated message can include content from the locally customizable `messages.json` file, while group identifiers originate from `wacli` output. If either source contains shell syntax, the shell may interpret it as a separate command rather than as literal WhatsApp message data. The application does not need shell parsing for its declared functionality. `wacli` can be invoked directly with an argument array, so use of `shell=True` exceeds the minimum execution capability required. ### Attack Path ...[truncated 1376 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `shell=True` and pass each command as an argument list: ```python def run_wacli_command(args): return subprocess.run( args, shell=False, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60, check=False, ) run_wacli_command([ "wacli", "messages", "list", "--chat", group_jid, "--after", today, "--json", ]) run_wacli_command([ "wacli", "send", "text", "--message", msg, "--to", group_jid, ]) ``` - Validate group JIDs against the exact WhatsApp JID syntax expected by `wacli`. - Validate custom dictionary files against a strict JSON schema. - Run the Skill under a dedicated, minimally privileged account. - Add tests containing quotation marks, command substitutions, semicolons, and newline characters to confirm they are treated as literal argument content. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/whatsapp_happybday.py:237
Finding
Plaintext Retention of Private WhatsApp Group Messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/whatsapp_happybday.py`, lines 138-159, 237-243, and 313 **Vulnerability Type**: Excessive collection and insecure local storage of sensitive message content **Risk Level**: Medium ### Vulnerable Code ```python def get_recent_messages(group_jid, today): """Get recent messages from a group (Text fields only)""" cmd = f'wacli messages list --chat "{group_jid}" --after {today} --json' stdout, stderr, rc = run_wacli_command(cmd) if rc != 0 or not stdout.strip(): return [] try: response = json.loads(stdout) data_block = response.get("data", {}) messages_list = data_block.get("messages", []) if not messages_list: return [] texts = [ msg["Text"] for msg in messages_list if msg.get("Text") and msg["Text"].strip() ] return texts ``` ```python for message in messages: if message in processed_today: continue processed_today.append(message) score = calculate_message_score(message) if score <= 0: continue ``` ```python def save_state(state): """Save state file""" os.makedirs(DATA_DIR, exist_ok=True) with open(STATE_FILE, 'w') as f: json.dump(state, f, indent=2) ``` ```python save_state(state) ``` ### Technical Analysis The Skill retrieves the text of every recent message in every enumerated group. Each previously unseen message is appended to `processed_today` before relevance scoring occurs. Consequently, messages with no birthday relevance are also retained. The state object is serialized to `data/name_counter.json` in plaintext. The file is created using the process's default permissions and no explicit restrictive mode is applied. Full message bodies are not required to prevent duplicate processing; stable message IDs or privacy-preserving digests would be sufficient. Although old state is intended to be purged after seve ...[truncated 1142 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not store complete message bodies. - Use the stable message identifier returned by `wacli` to track processing. - If no identifier is available, store a keyed HMAC of the group ID and message metadata rather than raw content. - Append a tracking value only after determining that tracking is necessary. - Create the state file atomically with permissions restricted to the owner, such as mode `0600`. - Apply restrictive permissions to the data directory, such as mode `0700`. - Reduce retention to the shortest practical duration and document it clearly. - Ensure backups and diagnostic output do not capture the state file. ]]>

other

Warning
Location
scripts/whatsapp_happybday.py:121
Finding
Unscoped Monitoring of All WhatsApp Groups<![CDATA[ ## Vulnerability Details **File Location**: `scripts/whatsapp_happybday.py`, lines 121-132 and 309-311 **Vulnerability Type**: Privacy overcollection and excessive access scope **Risk Level**: Medium ### Vulnerable Code ```python def get_groups(): """Get list of WhatsApp group JIDs""" stdout, stderr, rc = run_wacli_command("wacli chats list --json") if rc != 0 or not stdout.strip(): return [] try: response = json.loads(stdout) chats = response.get("data") if not chats: return [] jid_list = [ chat["JID"] for chat in chats if chat.get("Kind") == "group" and "JID" in chat ] return jid_list ``` ```python groups = get_groups() if not groups: print("❌ No groups found") return for group in groups: state = process_group(group, state) ``` ### Technical Analysis The Skill automatically enumerates every WhatsApp chat classified as a group and processes all of them. There is no group allowlist, denylist, consent control, or least-privilege selection mechanism. Birthday monitoring does not inherently require access to every group associated with the WhatsApp account. The implementation therefore accesses a broader set of conversations than necessary. This broad scope also amplifies the plaintext-retention issue because unrelated groups are included automatically. The behavior is disclosed in the documentation, but disclosure alone does not provide per-group consent or enforce least privilege. ### Attack Path 1. A user authenticates `wacli` with a WhatsApp account. 2. The Skill runs manually or through the recommended recurring schedule. 3. `get_groups()` obtains every group JID available to that account. 4. `process_group()` retrieves and analyzes current-day messages from each group. 5. Unrelated conversations are processed and potentially retained even when the user intended monitoring for only a limited set of groups. ...[truncated 538 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Add a required `BIRTHDAY_GROUP_ALLOWLIST` containing explicitly approved group JIDs. - Default to processing no groups until the user selects them. - Provide a command that lists group names and JIDs without automatically enabling monitoring. - Record explicit user approval for each monitored group. - Consider a denylist only as a secondary control; an allowlist is safer. - Avoid exposing group identifiers or message content in routine output. - Clearly document the exact data accessed, retained, and transmitted for each enabled group. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unpinned Third-Party Dependency<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, line 1 **Vulnerability Type**: Unversioned dependency and non-reproducible package installation **Risk Level**: Medium ### Vulnerable Code ```text python-dotenv ``` The documented installation command is: ```bash pip install -r requirements.txt ``` ### Technical Analysis The dependency is specified without an exact version or integrity hash. Every installation may therefore resolve to a different release from the configured Python package index. This does not prove that the current `python-dotenv` package is malicious. The risk is that a compromised package-index account, a future malicious release, an unsafe private package mirror, or an incompatible update could introduce code that executes during installation or import. Because the Skill imports `dotenv` when the script starts, malicious package code could also execute during normal scheduled operation. ### Attack Path 1. The user follows the documented installation command. 2. `pip` resolves the latest package version available from the configured index or mirror. 3. A compromised or malicious release is downloaded because neither a reviewed version nor a cryptographic hash is required. 4. Package installation hooks or imported package code execute under the user's account. 5. Malicious code gains access to the files, environment, and WhatsApp-related resources available to the OpenClaw process. ### Impact Assessment A compromised dependency could execute arbitrary Python code with the privileges of the installing or running user. It could read OpenClaw configuration, environment variables, WhatsApp data, and local files accessible to that account. The absence of version and hash constraints also reduces build reproducibility and may introduce unexpected compatibility or security regressions. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `python-dotenv` to a specifically reviewed version. - Generate and enforce package hashes, for example: ```text python-dotenv==REVIEWED_VERSION \ --hash=sha256:REVIEWED_DISTRIBUTION_HASH ``` - Install with: ```bash python -m pip install --require-hashes -r requirements.txt ``` - Review dependency updates before changing the pinned version. - Use a trusted package index and verify the expected package source. - Run dependency vulnerability scanning as part of release validation. - Consider eliminating the dependency and implementing narrowly scoped configuration loading if its functionality is not essential. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/whatsapp_happybday.py:50
Finding
Environment Configuration Loaded After Security-Relevant Values Are Read<![CDATA[ ## Vulnerability Details **File Location**: `scripts/whatsapp_happybday.py`, lines 50-80 and 282-284 **Vulnerability Type**: Security configuration initialization error **Risk Level**: Low ### Vulnerable Code ```python # --- Main Configuration --- SKIP_LIST_RAW = os.environ.get("BIRTHDAY_SKIP_LIST", "") def parse_skip_list(raw_string): permanent_skips = set() birthday_skips = {} if not raw_string.strip(): return permanent_skips, birthday_skips entries = [entry.strip() for entry in raw_string.split(",")] for entry in entries: if "|" in entry: name, date = entry.split("|", 1) name = name.strip().lower() date = date.strip() birthday_skips[(name, date)] = True else: permanent_skips.add(entry.strip().lower()) return permanent_skips, birthday_skips SKIP_LIST_PERMANENT, SKIP_LIST_BIRTHDAY = parse_skip_list(SKIP_LIST_RAW) MIN_MESSAGES = int(os.environ.get("BIRTHDAY_MIN_MESSAGES", "3")) CONFIDENCE_THRESHOLD = int(os.environ.get( "BIRTHDAY_CONFIDENCE_THRESHOLD", "120" )) ``` ```python def main(): """Main function""" print("="*60 + "\n🎉 WhatsApp HappyBDay - Score-Based Monitor\n" + "="*60) load_dotenv() state = load_state() ``` ### Technical Analysis The skip list, minimum-message threshold, and confidence threshold are read at module import time. The `.env` file is not loaded until `main()` executes, after those global values have already been initialized. As a result, values documented as configurable through `.env` do not affect these settings unless they were already exported into the parent process environment. In particular, configured exclusions can be silently ignored. `BIRTHDAY_SIMULATE` is evaluated later during processing, so a `.env` file can disable simulation while the skip list and threshold settings from the same file remain ignored. This inconsistent initialization can cause the script to se ...[truncated 1039 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Call `load_dotenv()` before reading any environment-backed setting: ```python from dotenv import load_dotenv load_dotenv() SKIP_LIST_RAW = os.environ.get("BIRTHDAY_SKIP_LIST", "") MIN_MESSAGES = int(os.environ.get("BIRTHDAY_MIN_MESSAGES", "3")) CONFIDENCE_THRESHOLD = int( os.environ.get("BIRTHDAY_CONFIDENCE_THRESHOLD", "120") ) ``` - Prefer loading configuration inside a dedicated function invoked from `main()`. - Validate integer ranges and reject invalid values with a clear error. - Parse booleans strictly rather than treating all non-`true` strings as false. - Print a non-sensitive startup summary showing whether simulation is enabled and how many groups are approved. - Add tests proving that `.env` skip entries and thresholds take effect before any group processing or message transmission. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (16)

Missing User Warnings

High
Confidence
97% confidence
Finding
The description emphasizes convenience and automation but does not clearly warn users that the skill continuously monitors WhatsApp groups and may automatically send messages on their behalf. This weakens informed consent and makes it more likely a user enables the skill without understanding the privacy and reputational consequences.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def run_wacli_command(cmd):
    """Execute wacli command"""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=60)
        return result.stdout, result.stderr, result.returncode
    except Exception as e:
        return "", str(e), 1
Confidence
99% confidence
Finding
This is a true tool-parameter abuse issue because the shell command wrapper is reused for multiple wacli operations and receives dynamic parameters incorporated into a shell string. The skill context makes it more dangerous because inputs originate from chat metadata and generated messages; a crafted group JID or message containing shell-breaking characters could pivot a messaging automation tool into arbitrary OS command execution.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**Version 1.0.2 - Security & Consistency Fixes**

**Monitor WhatsApp groups to dynamically detect people who should be congratulated. It identifies keywords (e.g., "birthday", "congratulations") and the person's name using a score-based system, then automatically sends a random customizable congratulatory message.**

## 🎯 Features
Confidence
93% confidence
Finding
The skill is designed to autonomously decide when a person should be congratulated and then send a message without human review. In a private-group messaging environment, this can misfire on jokes, sarcasm, or unrelated mentions, causing spam, disclosure of inferred personal events, or inappropriate automated participation in conversations.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README explicitly describes monitoring WhatsApp groups and automatically sending messages, but it does not clearly warn users that the skill accesses private group communications and may process personal data such as names and message content. In a messaging context, missing privacy and consent guidance can lead to unauthorized surveillance-like use, accidental policy violations, and improper handling of sensitive conversational data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares capabilities that imply environment access, file reads/writes, and shell execution but does not restrict them with explicit tool scopes or permissions. In combination with autonomous WhatsApp monitoring and message sending, this gives the skill unnecessarily broad operational authority and increases the blast radius if the script is modified, misused, or prompted indirectly.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
---
name: whatsapp-happybday
version: 1.0.2
description: Monitor WhatsApp groups to dynamically detect people who should be congratulated. It identifies keywords (e.g., "birthday", "congratulations") and the person's name using a score-based system, then automatically sends a random customizable congratulatory message.
triggers:
  - whatsapp happybday
  - monitor whatsapp group
Confidence
93% confidence
Finding
The skill is explicitly designed to autonomously decide when someone should be congratulated and to send a message without a per-message user review. Autonomous decision-making over private communications is risky because false positives, manipulation by group participants, or misidentification of names can cause unauthorized or embarrassing messages to be sent.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases are broad and action-oriented, such as monitoring WhatsApp groups or sending congratulations, which increases the chance of accidental activation in unrelated conversations. Because this skill can monitor communications and send messages automatically, unintended invocation can lead to privacy-impacting behavior or unauthorized outbound communication.

Session Persistence

Medium
Category
Rogue Agent
Content
To make the skill work, you need to configure a few environment variables and (optionally) customize the dictionaries.

### 1. Environment Variables (`.env`)
Create a `.env` file in the skill's root directory (`~/.openclaw/skills/whatsapp-happybday/.env`) or export these variables in your environment:

```bash
# Skip list with enhanced format:
Confidence
60% 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
```

### 2. Automated Execution (Cron Job)
Create a cron job to run the monitor script periodically (e.g., every hour between 8 AM and 8 PM). Run this in your terminal:

```bash
openclaw cron add \
Confidence
91% confidence
Finding
This finding is substantively the same scheduled-execution issue: the skill instructs the user to install a cron job that keeps the behavior active across sessions. In this context, persistence is more dangerous because the scheduled task performs monitoring and may send external messages, turning a one-time setup into ongoing autonomous action.

Session Persistence

Medium
Category
Rogue Agent
Content
```

### 2. Automated Execution (Cron Job)
Create a cron job to run the monitor script periodically (e.g., every hour between 8 AM and 8 PM). Run this in your terminal:

```bash
openclaw cron add \
Confidence
91% confidence
Finding
This finding is substantively the same scheduled-execution issue: the skill instructs the user to install a cron job that keeps the behavior active across sessions. In this context, persistence is more dangerous because the scheduled task performs monitoring and may send external messages, turning a one-time setup into ongoing autonomous action.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if sys.executable == '/opt/homebrew/bin/python3':
    # Try to find the correct python-dotenv installation
    import subprocess
    result = subprocess.run(['which', 'python3'], capture_output=True, text=True)
    if result.returncode == 0:
        python_path = result.stdout.strip()
        print(f"Using Python: {python_path}")
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
def run_wacli_command(cmd):
    """Execute wacli command"""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=60)
        return result.stdout, result.stderr, result.returncode
    except Exception as e:
        return "", str(e), 1
Confidence
98% confidence
Finding
The helper uses `subprocess.run(cmd, shell=True, ...)` on command strings that are later built with interpolated values such as `group_jid` and message text. If any of those values contain shell metacharacters or quotes, an attacker controlling WhatsApp-derived data or configuration could trigger shell command injection and execute arbitrary commands on the host.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script reads and analyzes recent WhatsApp group messages to infer personal events without presenting any user-facing privacy notice or consent mechanism. In this context, monitoring private group chats and extracting names increases privacy risk and may violate participant expectations or platform policy, especially since the data is persisted in local state.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script can automatically send WhatsApp messages once scoring thresholds are met, with no explicit runtime confirmation or user-facing warning at the moment of transmission. In this skill context, that can lead to unintended messaging, harassment, reputational harm, or spam if detection is wrong or manipulated by group participants.

Unpinned Dependencies

Low
Category
Supply Chain
Content
python-dotenv
Confidence
95% confidence
Finding
The dependency is specified without a version pin, which makes builds non-reproducible and can cause the skill to install different releases over time, including vulnerable or breaking versions. In a skill that may process WhatsApp-derived content and run unattended, unexpected dependency changes increase supply-chain risk and operational unpredictability.

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding
The manifest does not pin python-dotenv, so it is impossible to determine whether installation will resolve to a version affected by known advisories. While requirements.txt alone does not prove exploitation, the lack of version control means a vulnerable release could be installed, which is a real supply-chain weakness.

Static analysis

No suspicious patterns detected.