Back to skill

Security audit

Multi-Modal Content Creator

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its stated WhatsApp-to-OpenAI content workflow, but it handles tokens, local files, and automatic replies with too little containment or user control.

Review before installing. Use it only in an isolated environment, avoid real WhatsApp/customer data until token storage and media-path validation are fixed, pin dependencies, and add a confirmation or approval step before sending automated replies.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
workflow.py:17
Finding
Untrusted Audio Paths Can Cause Local Files to Be Uploaded to OpenAI<![CDATA[ ## Vulnerability Details **File Location**: `workflow.py:17-22`; `transcribe.py:17-31` **Vulnerability Type**: Unvalidated local file access and external data disclosure **Risk Level**: High ### Vulnerable Code ```python # workflow.py:17-22 if message["type"] == "text": prompt = message["content"] elif message["type"] == "audio": # Transcribe audio to get prompt print(f"Transcribing audio file: {message['content']}") prompt = transcribe_audio(message["content"]) ``` ```python # transcribe.py:17-31 audio = AudioSegment.from_file(file_path) chunk_size_ms = chunk_size_mins * 60 * 1000 chunks = [audio[i:i + chunk_size_ms] for i in range(0, len(audio), chunk_size_ms)] full_transcript = "" for i, chunk in enumerate(chunks): buffer = io.BytesIO() buffer.name = f"chunk_{i}.mp3" chunk.export(buffer, format="mp3") buffer.seek(0) transcript = client.audio.transcriptions.create( model="whisper-1", file=buffer, response_format="text", ) ``` ### Technical Analysis The workflow treats the `content` property of an audio message as a trusted local file path. It passes that path directly to `AudioSegment.from_file()` without canonicalizing it, restricting it to a controlled media directory, rejecting symbolic links, or validating its ownership and origin. After opening the file, the application converts the audio into MP3 chunks and uploads those chunks to the OpenAI transcription API. Consequently, any readable file that can be decoded as supported media may be disclosed to an external service. The implementation also has no input file-size, decoded-duration, chunk-count, or aggregate upload limit. A large or specially prepared media file could consume significant memory, CPU time, network bandwidth, and paid API quota. The current WhatsApp client returns hard-coded messages, but this flaw becomes directly exploitable if it is replaced with the real incoming-message integration described by the ...[truncated 1644 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not accept local filesystem paths directly from incoming messages. Use an internal, server-generated media identifier and resolve it through a trusted download or storage component. 2. Store inbound media in a dedicated directory that is inaccessible to untrusted users. 3. Resolve the candidate path with `Path.resolve()` and verify that it remains beneath the approved media root. 4. Reject absolute paths, traversal sequences, symbolic links, device files, pipes, sockets, and other non-regular files. 5. Open files using mechanisms that prevent symbolic-link following where supported. 6. Verify that the file was created by the trusted inbound-media component and has appropriate ownership and permissions. 7. Enforce limits on compressed file size, decoded duration, chunk count, and total bytes sent to the API. 8. Validate the media container and codec rather than relying only on its filename. 9. Process decoding in a sandbox with restricted filesystem and network access. 10. Require explicit user or administrator authorization before transmitting sensitive audio to an external service. 11. Record auditable metadata about the source message and upload without logging the sensitive content itself. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
wacli.py:10
Finding
WhatsApp Authentication Token Is Accepted Through Process Arguments and Stored in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `wacli.py:10-29`; `wacli.py:102-108` **Vulnerability Type**: Insecure credential handling and plaintext secret storage **Risk Level**: Medium ### Vulnerable Code ```python # wacli.py:10-29 def __init__(self, config_path=None): self.config_path = config_path or os.path.expanduser("~/.wacli/config.json") self.load_config() def load_config(self) -> None: if os.path.exists(self.config_path): with open(self.config_path, "r") as f: self.config = json.load(f) else: self.config = {"auth_token": None, "default_number": None} def save_config(self) -> None: os.makedirs(os.path.dirname(self.config_path), exist_ok=True) with open(self.config_path, "w") as f: json.dump(self.config, f, indent=2) def login(self, auth_token: str) -> None: """Authenticate with WhatsApp CLI service""" self.config["auth_token"] = auth_token self.save_config() print("Login successful") ``` ```python # wacli.py:102-108 command = sys.argv[1] if command == "login": if len(sys.argv) != 3: print("Usage: wacli login <auth_token>") return client.login(sys.argv[2]) ``` ### Technical Analysis The login command accepts the authentication token as a command-line argument. Command-line secrets can be exposed through shell history, process inspection utilities, terminal session logging, debugging information, and administrative monitoring systems. The token is then serialized directly into `~/.wacli/config.json` as plaintext. Although the process's `umask` may incidentally create a restrictive file on some systems, the code does not explicitly enforce permissions on either the `~/.wacli` directory or the configuration file. Existing files with permissive modes are also overwritten without correcting those modes. Anyone who can read the configuration file can recover the complete authentication token. The current WhatsApp operations are simulated, but ...[truncated 1395 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not accept authentication tokens as command-line arguments. 2. Read the token through a non-echoing interactive prompt such as `getpass.getpass()`, a protected environment injection mechanism, or an operating-system credential store. 3. Prefer a platform keychain or dedicated secret manager instead of a plaintext JSON file. 4. If local storage is unavoidable, create `~/.wacli` with mode `0700` and the token file with mode `0600`. 5. Use secure file creation flags that prevent following symbolic links and avoid predictable temporary files. 6. Verify ownership and permissions before reading an existing configuration file. 7. Correct overly permissive modes before writing sensitive data. 8. Store non-secret preferences separately from credentials. 9. Add token revocation and rotation procedures, and immediately rotate any token previously passed through the command line. 10. Ensure logs and error messages never include the token. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Dependencies Are Installed Without Exact Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-4`; `SKILL.md:26-29` **Vulnerability Type**: Non-reproducible and integrity-unverified dependency installation **Risk Level**: Medium ### Vulnerable Code ```text # requirements.txt:1-4 openai>=1.0.0 pydub>=0.25.1 requests>=2.31.0 python-dotenv>=1.0.0 ``` ```bash # SKILL.md:26-29 pip install -r requirements.txt export OPENAI_API_KEY="your-api-key" python wacli.py login <your-wacli-token> ``` ### Technical Analysis Every dependency uses an open-ended lower-bound constraint. Installation can therefore select any newer compatible release available from the configured package index, as well as unconstrained transitive dependencies. The project supplies no lock file, package hashes, trusted index restriction, or reproducible environment definition. As a result, the code reviewed during the audit is not sufficient to determine the exact third-party code that will execute after users follow the installation instructions. A compromised future release, maliciously replaced artifact, unsafe transitive dependency, or unexpectedly incompatible major version could be installed without any project change. Python package installation may execute build backends or setup logic, and imported dependencies execute code with the privileges of the user running the Skill. `python-dotenv` is listed but is not imported by the reviewed source, unnecessarily increasing the dependency surface. ### Attack Path 1. A user follows the documented `pip install -r requirements.txt` command. 2. The package resolver queries its configured package index and selects the newest releases satisfying the lower bounds. 3. A selected direct or transitive package contains compromised installation logic, malicious runtime code, or a newly introduced vulnerability. 4. The package executes during build, installation, or subsequent import. 5. The dependency code runs with the installing or application user's permissions and can a ...[truncated 941 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to an exact, reviewed version. 2. Generate a lock file that includes all transitive dependencies. 3. Require cryptographic hashes for downloaded artifacts, such as through a hash-locked requirements file and `pip --require-hashes`. 4. Restrict installation to approved HTTPS package indexes and trusted package sources. 5. Review dependency updates before regenerating the lock file. 6. Run dependency vulnerability and license scanning in continuous integration. 7. Install packages in an isolated virtual environment as a non-administrative user. 8. Prefer prebuilt, verified wheels and control whether source distributions and build isolation are permitted. 9. Remove the unused `python-dotenv` dependency unless it is required by documented functionality. 10. Maintain an auditable process for periodically upgrading pinned versions so security fixes are adopted deliberately. ]]>
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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Tainted flow: 'image_url' from os.environ.get (line 32, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
revised_prompt = response.data[0].revised_prompt

        # Download and save image
        img_response = requests.get(image_url, timeout=30)
        img_response.raise_for_status()
        img_data = img_response.content
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill is described as an end-to-end content creation pipeline, but the analyzed behavior suggests it may primarily implement WhatsApp CLI functions and local auth/config storage instead. Undeclared authentication storage and operational behavior increase risk because users may expose tokens or enable automation without understanding where sensitive data is kept or what actions the skill will actually perform.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill is described as an end-to-end content creation pipeline, but the analyzed behavior suggests it may primarily implement WhatsApp CLI functions and local auth/config storage instead. Undeclared authentication storage and operational behavior increase risk because users may expose tokens or enable automation without understanding where sensitive data is kept or what actions the skill will actually perform.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill is described as an end-to-end content creation pipeline, but the analyzed behavior suggests it may primarily implement WhatsApp CLI functions and local auth/config storage instead. Undeclared authentication storage and operational behavior increase risk because users may expose tokens or enable automation without understanding where sensitive data is kept or what actions the skill will actually perform.

Exfiltration Commands

High
Category
Prompt Injection
Content
return mock_messages
    
    def send_message(self, to: str, content: str, media_path: str | None = None) -> bool:
        """Send message to WhatsApp number"""
        if not self.config["auth_token"]:
            print("Error: Not logged in. Use `wacli login <token>` first.")
            return False
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises and instructs use of capabilities that access environment secrets, local files, and the network, but it does not declare any tool scope or permissions boundaries. This creates a transparency and control gap: an agent or reviewer cannot easily constrain the skill, increasing the chance of overbroad access to API keys, local data, or external services.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill description lacks clear warnings that it may automatically process incoming WhatsApp content and send replies on the user's behalf. In a messaging context, missing consent and automation disclosures can lead to privacy issues, accidental outbound communications, and misuse of personal or customer data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code sends user-provided audio chunks to the external OpenAI Whisper API for transcription, which is a real data-disclosure risk if users are not clearly informed and have not consented. In this skill’s context—automated WhatsApp processing of text or voice messages—the likelihood of handling personal, sensitive, or regulated content is elevated, making undisclosed third-party transfer more dangerous.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The login flow stores the authentication token in a plaintext JSON config file under the user's home directory without any warning, encryption, or permission hardening. If the local machine, home directory, backups, or workspace are accessible to other users or malware, the token can be recovered and used to access the WhatsApp service account.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The workflow forwards user-supplied WhatsApp content to external AI processing components: audio is sent for transcription and prompts are sent for image generation, but the code shows no consent, notice, or privacy gating before that transfer. Because WhatsApp messages may contain personal or sensitive information, this creates a real privacy and data-sharing risk, especially in an automated end-to-end workflow that processes incoming messages without user confirmation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
pydub>=0.25.1
requests>=2.31.0
python-dotenv>=1.0.0
Confidence
95% confidence
Finding
The dependency is specified with only a lower bound, which allows future unreviewed versions to be installed and makes builds non-reproducible. In a workflow that processes external WhatsApp content and calls third-party APIs, this increases supply-chain risk and makes it harder to ensure deployed environments are not exposed to newly introduced vulnerable or breaking releases.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
pydub>=0.25.1
requests>=2.31.0
python-dotenv>=1.0.0
Confidence
95% confidence
Finding
Using an unpinned pydub version permits installation of arbitrary newer releases, reducing reproducibility and increasing exposure to supply-chain or compatibility issues. Because this skill handles audio transcription inputs, unexpected dependency changes in media-processing libraries could affect parsing behavior or introduce exploitable flaws from upstream.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
pydub>=0.25.1
requests>=2.31.0
python-dotenv>=1.0.0
Confidence
98% confidence
Finding
The requests dependency is unpinned, so environments may resolve to different versions, including versions affected by known advisories. Since this skill likely makes outbound HTTP requests as part of automated messaging and API workflows, a vulnerable or behavior-changing requests release could expose credentials, alter TLS behavior, or otherwise increase remote attack surface.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding
The manifest does not pin requests, and that package has multiple known advisories across versions, so it is impossible to verify from this file whether the deployed version is safe. This is more concerning in this skill because network communication is central to its operation, increasing the chance that any requests-related flaw could be exercised through remote interaction or credential-bearing API calls.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
pydub>=0.25.1
requests>=2.31.0
python-dotenv>=1.0.0
Confidence
94% confidence
Finding
An unpinned python-dotenv package allows uncontrolled version drift and complicates assurance that only reviewed releases are installed. In a skill that likely relies on environment-based secrets for API keys and messaging credentials, dependency instability in dotenv handling can increase configuration and supply-chain risk.

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
Because python-dotenv is not pinned and has known advisories in some versions, the file does not provide enough assurance that the installed release is not vulnerable. Given this skill likely loads secrets from environment files, weaknesses in dotenv handling could affect confidentiality or integrity of configuration if unsafe versions are installed or local file handling is exposed.

Static analysis

No suspicious patterns detected.