Back to skill

Security audit

TopMediai TTS

Security checks for vulnerabilities and agentic risk

Overview

This TopMediai text-to-speech skill is coherent, but it can send the configured API key and user text to an environment-controlled API host without validating that host.

Install only if you trust the skill publisher and your local .env configuration. Keep TOPMEDIAI_BASE_URL at https://api.topmediai.com unless you deliberately use a trusted test endpoint, avoid submitting sensitive text unless approved for TopMediai processing, and prefer pinned dependencies before production use.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/topmediai_tts_api.py:20
Finding

Unvalidated API Base URL Can Redirect Credentials and TTS Content

Content
View full analysis
Dict[str, str]: key = api_key or DEFAULT_KEY if not key: raise RuntimeError( "TOPMEDIAI_API_KEY not configured. Edit: {} and set TOPMEDIAI_API_KEY=YOUR_KEY.".format(_ENV_PATH) ) return {"x-api-key": key, "Content-Type": "application/json"} ``` ```python def text_to_speech(text: str, speaker: str, emotion: Optional[str] = None, api_key: Optional[str] = None) -> Dict[str, Any]: url = f"{BASE_URL}/v1/text2speech" headers = _headers(api_key) payload: Dict[str, Any] = { "text": text, "speaker": speaker, } if emotion: payload["emotion"] = emotion _debug_request("POST", url, headers=headers, payload=payload) try: r = requests.post(url, json=payload, headers=headers, timeout=120) r.raise_for_status() return r.json() except Exception as e: _raise_as_runtime_error(e, "POST", url) ``` The same base URL and credential-bearing headers are also used by the account-information and voice-list requests. ### Technical Analysis `TOPMEDIAI_BASE_URL` is accepted directly from the process environment or project `.env` file without validating its scheme, hostname, port, or embedded credentials. The application subsequently attaches the TopMediai API key as an `x-api-key` header to requests sent to that destination. Consequently, anyone able to alter the process environment or `.env` configuration can redirect requests to an attacker-controlled endpoint. For TTS operations, both the API key and the user-supplied tex ...[truncated 1712 chars]
Remediation
View remediation
str: parsed = urlparse(value) if parsed.scheme != "https": raise RuntimeError("TOPMEDIAI_BASE_URL must use HTTPS") if parsed.hostname not in ALLOWED_API_HOSTS: raise RuntimeError("TOPMEDIAI_BASE_URL host is not approved") if parsed.username or parsed.password or parsed.fragment: raise RuntimeError("TOPMEDIAI_BASE_URL contains unsupported components") if parsed.port not in (None, 443): raise RuntimeError("TOPMEDIAI_BASE_URL uses an unapproved port") return value.rstrip("/") BASE_URL = validate_base_url( os.environ.get("TOPMEDIAI_BASE_URL", "https://api.topmediai.com") ) ``` ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Third-Party Dependencies Are Not Reproducibly Pinned

Content
View full analysis
=2.31.0 python-dotenv>=1.0.1 ``` The documented installation procedure in `README.md:12` installs these open-ended constraints directly: ```text pip install -r requirements.txt ``` ### Technical Analysis The dependencies use minimum-version constraints without upper bounds, exact pins, a lock file, or package hashes. Each installation can therefore resolve to a different future release. This prevents reproducible builds and means that a newly published, compromised, or incompatible dependency version can enter the environment without a corresponding review of this project. The package names are established packages and there is no evidence in the audited files that either dependency is currently malicious. The risk arises from unconstrained future dependency resolution and the absence of integrity verification. ### Attack Path 1. A future dependency release is compromised, maliciously published, or otherwise introduces unsafe behavior. 2. A user follows the documented command `pip install -r requirements.txt`. 3. The package resolver selects that newer release because it satisfies the `>=` constraint. 4. The dependency is installed without comparison against an approved hash or lock file. 5. The package's code executes during import or when the skill performs HTTP requests or loads environment configuration. ### Impact Assessment Dependency code runs with the privileges of the Python process executing the skill. A compromised dependency could potentially: - Read the TopMediai API key and other environment variables. - Read files accessible to the skill process. - Alter or intercept network requests and TTS content. - Execute arbitrary Python behavior under the invoking user's account. The practical risk is reduced because exploi ...[truncated 141 chars]
Remediation
View remediation
python-dotenv== ``` 2. Generate a lock file using a dependency-management tool such as `pip-tools`, Poetry, or uv. 3. Record and verify package hashes. For pip-based deployments, use a fully resolved requirements file and install it with: ```text pip install --require-hashes -r requirements.lock ``` 4. Include transitive dependencies in the lock file rather than pinning only direct dependencies. 5. Run dependency vulnerability scanning in continuous integration using tools such as `pip-audit`. 6. Use a trusted package index and prevent unreviewed fallback to alternative indexes. 7. Establish a controlled update process that reviews release notes, security advisories, and test results before changing locked versions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (15)

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The request URL is derived from TOPMEDIAI_BASE_URL, an environment-controlled value, and the code sends the x-api-key header to whatever host that value points to. If an attacker can influence the environment or bundled .env file, they can redirect requests to an attacker-controlled server and exfiltrate the API key and user content.

Content

Scanner excerpt · scripts/topmediai_tts_api.py (reported line 72)May include surrounding context.

python
headers = _headers(api_key)
    _debug_request("GET", url, headers=headers)
    try:
        r = requests.get(url, headers=headers, timeout=30)
        r.raise_for_status()
        return r.json()
    except Exception as e:

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

This GET call uses a URL built from the environment-supplied BASE_URL and includes the API key header. A malicious or compromised environment configuration can cause credential-bearing requests to be sent to an unintended destination, enabling secret disclosure and request hijacking.

Content

Scanner excerpt · scripts/topmediai_tts_api.py (reported line 84)May include surrounding context.

python
headers = _headers(api_key)
    _debug_request("GET", url, headers=headers)
    try:
        r = requests.get(url, headers=headers, timeout=60)
        r.raise_for_status()
        return r.json()
    except Exception as e:

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

Like the other API methods, this function trusts an environment-derived base URL and transmits the x-api-key to that endpoint. In a skill context, local configuration is often easier to tamper with than application code, so this becomes a realistic credential exfiltration vector.

Content

Scanner excerpt · scripts/topmediai_tts_api.py (reported line 96)May include surrounding context.

python
headers = _headers(api_key)
    _debug_request("GET", url, headers=headers)
    try:
        r = requests.get(url, headers=headers, timeout=60)
        r.raise_for_status()
        return r.json()
    except Exception as e:

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

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The POST target is environment-controlled and receives both the API key and the user's TTS text payload. If BASE_URL is altered, the skill can leak sensitive user-provided text and credentials to an attacker-controlled endpoint, which is especially relevant because this function handles the most sensitive content.

Content

Scanner excerpt · scripts/topmediai_tts_api.py (reported line 123)May include surrounding context.

python
payload["emotion"] = emotion
    _debug_request("POST", url, headers=headers, payload=payload)
    try:
        r = requests.post(url, json=payload, headers=headers, timeout=120)
        r.raise_for_status()
        return r.json()
    except Exception as e:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · .env (reported line 1)May include surrounding context.

text
# Copy to .env and fill your key
TOPMEDIAI_API_KEY=YOUR_KEY
TOPMEDIAI_BASE_URL=https://api.topmediai.com

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/topmediai_tts_api.py (reported line 16)May include surrounding context.

python
from pathlib import Path

_SKILL_ROOT = Path(__file__).resolve().parents[1]
_ENV_PATH = _SKILL_ROOT / ".env"
if _ENV_PATH.exists():
    load_dotenv(_ENV_PATH.as_posix())

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README explicitly describes sending user-provided text to TopMediai's remote TTS API but does not warn users that their prompts may leave the local environment and be processed by a third party. This can lead to accidental disclosure of sensitive, proprietary, or regulated data if users assume the skill operates locally or do not understand the privacy implications.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill requires environment access for TOPMEDIAI_API_KEY and performs outbound network requests, but it does not declare any tool scope such as permissions or allowed-tools. This weakens security review and user consent because the skill can access secrets and contact an external service without explicit capability declaration in the manifest.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends user-provided text and an API-key-authenticated request to an external TopMediai service, but the description does not clearly warn users about this data flow. This creates a privacy and transparency risk because users may unknowingly transmit sensitive prompts or content to a third party.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

When TOPMEDIAI_DEBUG is enabled, _debug_request logs the full payload, including user text submitted for speech generation. This can expose sensitive prompts or personal data in console logs, CI logs, or agent telemetry without any explicit user notice or redaction.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/topmediai_tts_api.py (reported line 123)May include surrounding context.

python
payload["emotion"] = emotion
    _debug_request("POST", url, headers=headers, payload=payload)
    try:
        r = requests.post(url, json=payload, headers=headers, timeout=120)
        r.raise_for_status()
        return r.json()
    except Exception as e:

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with a lower bound only (requests>=2.31.0), which allows future releases to be installed without review and prevents reproducible builds. This creates supply-chain risk and makes it unclear whether a vulnerable or breaking version could be resolved in different environments.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.31.0
python-dotenv>=1.0.1

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
90% confidence
Finding

requests has known advisories, and because the manifest does not pin an exact version, there is no way to verify from this file alone whether the installed version includes fixes. In a skill that likely makes outbound API calls, this uncertainty increases supply-chain and runtime risk, even though the file does not prove a specific vulnerable release is installed.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is not pinned to an exact version (python-dotenv>=1.0.1), so installations may resolve to different releases over time. That weakens reproducibility and can silently introduce vulnerable or incompatible versions into the skill.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.31.0
python-dotenv>=1.0.1

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

python-dotenv has advisories, but the unpinned requirement prevents verifying whether the resolved package version is affected. While this package is typically used for local configuration loading, leaving the version unconstrained still exposes the project to avoidable supply-chain uncertainty.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.