Back to skill

Security audit

Talking Circle

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but one cloud TTS path disables TLS verification while sending credentials and user text, which is unsafe enough to require review before installation.

Install only if you are comfortable with cloud TTS and local media processing. Avoid the SaluteSpeech mode until TLS verification is fixed, rotate any SaluteSpeech credentials already used on untrusted networks, and prefer pinned dependencies or a private verified environment before running the text-to-video scripts.

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

Error
Location
scripts/make_salute_text_to_video.py:12
Finding

SaluteSpeech Requests Disable TLS Certificate Verification

Content
View full analysis

Vulnerability Details

File Location: scripts/make_salute_text_to_video.py, lines 12–13 and 38–61
Vulnerability Type: Improper certificate validation for sensitive network requests
Risk Level: High

Vulnerable Code

python
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
python
def get_salute_token(auth_key: str, scope: str) -> str:
    """Get OAuth token from SaluteSpeech API. Token is valid for 30 minutes."""
    url = "https://ngw.devices.sberbank.ru:9443/api/v2/oauth"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Accept": "application/json",
        "RqUID": str(uuid.uuid4()),
        "Authorization": f"Basic {auth_key}",
    }
    r = requests.post(url, headers=headers, data={"scope": scope}, timeout=30, verify=False)
    r.raise_for_status()
    return r.json()["access_token"]


def synthesize_salute(token: str, text: str, voice: str, audio_format: str) -> bytes:
    """Synthesize speech via SaluteSpeech REST API."""
    url = "https://smartspeech.sber.ru/rest/v1/text:synthesize"
    params = {"format": audio_format, "voice": voice}
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/text",
    }
    r = requests.post(url, headers=headers, params=params, data=text.encode("utf-8"), timeout=90, verify=False)
    if r.status_code == 401:
        raise SystemExit("SaluteSpeech auth failed (401). Check your SALUTE_SPEECH_AUTH key.")
    if r.status_code == 429:
        raise SystemExit("SaluteSpeech rate limit (429). Try again later.")
    r.raise_for_status()
    return r.content

Technical Analysis

Both SaluteSpeech network requests explicitly set verify=False, disabling TLS certificate-chain and hostname validation. The code also globally suppresses InsecureRequestWarning, concealing the unsafe connection from users and ...[truncated 2419 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove verify=False from both requests.post calls so that Requests uses the operating system or certifi trust store by default.
  2. Remove the global urllib3.disable_warnings call. Certificate failures must remain visible and must terminate the operation.
  3. If the deployment requires a private certificate authority, configure an explicit trusted CA bundle:
    python
    requests.post(url, ..., verify="/path/to/trusted-ca-bundle.pem")
    
  4. Do not implement a fallback that retries with certificate validation disabled.
  5. Catch requests.exceptions.SSLError and report a clear error instructing the operator to repair the local trust configuration.
  6. Prefer environment-based credential input over --auth-key, because command-line arguments can be exposed through process listings and shell history.
  7. After remediation, rotate any SaluteSpeech credentials previously used over untrusted networks with the vulnerable implementation.
  8. Add automated tests that verify requests do not set verify=False and that invalid certificates cause the operation to fail closed.

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unpinned Dependencies Are Automatically Downloaded and Installed at Runtime

Content
View full analysis

Vulnerability Details

File Location: requirements.txt, lines 1–3; runtime installation in scripts/make_text_to_video.py, lines 17–29, and scripts/make_salute_text_to_video.py, lines 17–29
Vulnerability Type: Unpinned runtime dependency installation
Risk Level: Medium

Vulnerable Code

requirements.txt:

text
numpy
pillow
requests

The same installation pattern is used by both cloud TTS scripts:

python
def ensure_runtime_python() -> str:
    """Return python path with required video deps for make_talking_circle_video.py."""
    venv_dir = Path("/tmp/talking-circle-venv")
    py = venv_dir / "bin" / "python"
    if py.exists():
        return str(py)

    subprocess.run(["python3", "-m", "venv", str(venv_dir)], check=True)
    pip = venv_dir / "bin" / "pip"
    req = Path(__file__).resolve().parents[1] / "requirements.txt"
    subprocess.run([str(pip), "install", "--quiet", "-r", str(req)], check=True)
    return str(py)

Technical Analysis

The requirements file does not constrain dependency versions or distribution hashes. On the first text-to-video execution, the Skill creates a virtual environment and instructs pip to download and install whichever compatible releases are currently selected by the configured package index.

Python package installation can execute package build logic, and imported dependencies subsequently execute with the privileges of the user running the Skill. Consequently, the reviewed repository does not fully determine the code that will execute at runtime.

The listed dependency names are established packages, and the audit found no evidence of deliberate typosquatting or a malicious package source. The risk arises from unconstrained versions, lack of integrity verification, and automatic installation rather than from a confirmed malicious dependency.

The fixed shared path /tmp/talking-circle-venv also means the scripts trust any ...[truncated 1826 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin reviewed dependency versions rather than resolving arbitrary current releases:
    text
    numpy==REVIEWED_VERSION
    Pillow==REVIEWED_VERSION
    requests==REVIEWED_VERSION
    
  2. Generate and verify cryptographic hashes for every permitted distribution and install with:
    bash
    pip install --require-hashes -r requirements.txt
    
  3. Use a lock file produced by a dependency-management tool and review updates before deployment.
  4. Prefer explicit installation during Skill setup rather than silently downloading executable dependencies during normal operation.
  5. Display the package source and installation action to the operator instead of suppressing installation output with --quiet.
  6. Use a user-private cache or environment directory with restrictive permissions instead of the predictable shared /tmp/talking-circle-venv path.
  7. Validate the ownership and permissions of any reused environment, or recreate it from the locked dependency set when its integrity cannot be established.
  8. Configure trusted package indexes explicitly and use HTTPS with certificate verification.
  9. Add dependency scanning and scheduled review of pinned versions so security updates can be adopted deliberately.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The code clearly implements audio-driven talking-circle video creation from provided frame images and an input audio file, which aligns with part of the description. However, the declared description materially overstates capabilities by claiming text-to-video support via ElevenLabs or SaluteSpeech TTS. There is no text input handling, no network/API calls, no TTS provider integration, and no speech synthesis at all. The primary implemented purpose is narrower: audio-to-video avatar animation only. Therefore the description does not accurately represent the supplied code chunk.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

TLS certificate verification is explicitly disabled while requesting an OAuth token, allowing a man-in-the-middle attacker to intercept or alter the connection. Because this request carries a Basic authorization credential derived from client_id:client_secret, compromise could expose API credentials and enable fraudulent token issuance.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises behavior that requires shell execution, filesystem writes, environment-variable access, and network access, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this weakens least-privilege controls and can cause the skill to receive broader capabilities than users expect, increasing the blast radius if the skill is misused or later modified.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill encourages sending user text and authentication material to third-party TTS providers but does not provide a clear privacy warning about external transmission. Users may unknowingly expose sensitive prompts, voice content, or credentials to external services, which is especially risky if they assume the processing is local.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill tells the assistant to save generated avatar frames to persistent storage for reuse, but it does not clearly warn the user that reusable media files will remain on disk. Persistent storage of user-provided or generated likeness data can create privacy and retention risks, especially on shared machines or multi-user agent environments.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

This line installs packages at runtime from requirements.txt using pip, which introduces supply-chain risk if dependencies or package indexes are tampered with. In an agent skill context, automatic dependency installation expands the trust boundary and can lead to arbitrary code execution during package install.

Content

Scanner excerpt · scripts/make_salute_text_to_video.py (reported line 33)May include surrounding context.

python
subprocess.run(["python3", "-m", "venv", str(venv_dir)], check=True)
    pip = venv_dir / "bin" / "pip"
    req = Path(__file__).resolve().parents[1] / "requirements.txt"
    subprocess.run([str(pip), "install", "--quiet", "-r", str(req)], check=True)
    return str(py)

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
99% confidence
Finding

Using verify=False as the default for an authentication request is an unsafe default that weakens transport security for all users of the script. In this skill, the affected traffic includes OAuth credential exchange, making the weakness particularly dangerous.

Content

Scanner excerpt · scripts/make_salute_text_to_video.py (reported line 46)May include surrounding context.

python
"RqUID": str(uuid.uuid4()),
        "Authorization": f"Basic {auth_key}",
    }
    r = requests.post(url, headers=headers, data={"scope": scope}, timeout=30, verify=False)
    r.raise_for_status()
    return r.json()["access_token"]

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script sends user-supplied text to a remote TTS service while also disabling TLS certificate verification, so both privacy and integrity are at risk. An attacker on the network path could read sensitive text content, tamper with synthesized responses, or harvest the bearer token used for API access.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
98% confidence
Finding

Using verify=False for the TTS request makes insecure transport the default and exposes both bearer-token authentication and user text to interception. Given this skill's purpose is to process arbitrary user-provided speech content, the confidentiality risk is meaningful in normal use.

Content

Scanner excerpt · scripts/make_salute_text_to_video.py (reported line 59)May include surrounding context.

python
"Authorization": f"Bearer {token}",
        "Content-Type": "application/text",
    }
    r = requests.post(url, headers=headers, params=params, data=text.encode("utf-8"), timeout=90, verify=False)
    if r.status_code == 401:
        raise SystemExit("SaluteSpeech auth failed (401). Check your SALUTE_SPEECH_AUTH key.")
    if r.status_code == 429:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_salute_text_to_video.py (reported line 17)May include surrounding context.

python
def run(cmd):
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Command failed: {' '.join(str(c) for c in cmd)}\n{result.stderr}")
    return result

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_salute_text_to_video.py (reported line 165)May include surrounding context.

python
def run(cmd):
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Command failed: {' '.join(str(c) for c in cmd)}\n{result.stderr}")
    return result

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_talking_circle_video.py (reported line 13)May include surrounding context.

python
def run(cmd):
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Command failed: {' '.join(str(c) for c in cmd)}\n{result.stderr}")
    return result

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 12)May include surrounding context.

python
def run(cmd):
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Command failed: {' '.join(str(c) for c in cmd)}\n{result.stderr}")
    return result

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 190)May include surrounding context.

python
def run(cmd):
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Command failed: {' '.join(str(c) for c in cmd)}\n{result.stderr}")
    return result

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_salute_text_to_video.py (reported line 30)May include surrounding context.

python
if py.exists():
        return str(py)

    subprocess.run(["python3", "-m", "venv", str(venv_dir)], check=True)
    pip = venv_dir / "bin" / "pip"
    req = Path(__file__).resolve().parents[1] / "requirements.txt"
    subprocess.run([str(pip), "install", "--quiet", "-r", str(req)], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 25)May include surrounding context.

python
if py.exists():
        return str(py)

    subprocess.run(["python3", "-m", "venv", str(venv_dir)], check=True)
    pip = venv_dir / "bin" / "pip"
    req = Path(__file__).resolve().parents[1] / "requirements.txt"
    subprocess.run([str(pip), "install", "--quiet", "-r", str(req)], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
84% confidence
Finding

The script creates a venv in /tmp and installs dependencies at runtime with pip before executing them. In an agent or shared-host context, this expands the trust boundary to package resolution and a world-writable temporary location, creating supply-chain and local tampering risk if requirements or the temp venv are modified or if installation sources are not tightly controlled.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 28)May include surrounding context.

python
subprocess.run(["python3", "-m", "venv", str(venv_dir)], check=True)
    pip = venv_dir / "bin" / "pip"
    req = Path(__file__).resolve().parents[1] / "requirements.txt"
    subprocess.run([str(pip), "install", "--quiet", "-r", str(req)], check=True)
    return str(py)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 55)May include surrounding context.

python
d2.ellipse((center[0] - r, center[1] - r, center[0] + r, center[1] + r), outline=(210, 210, 210), width=2)
    final.save(out_img)

    subprocess.run(
        [
            "ffmpeg",
            "-y",

External Transmission

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The hardcoded ElevenLabs endpoint indicates this script is designed to send content off-host to an external vendor. In this skill's context, remote TTS is expected functionality, but it still represents a real privacy and data-governance risk if users are not clearly informed that their text is leaving the environment.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 134)May include surrounding context.

python
td = Path(td)
        audio = td / "speech.mp3"

        url = f"https://api.elevenlabs.io/v1/text-to-speech/{args.voice_id}?output_format=mp3_44100_128"
        payload = {
            "text": args.text,
            "model_id": args.model_id,

External Transmission

Medium
Category
Data Exfiltration
Confidence
98% confidence
Finding

This POST request sends user text to a third-party TTS API and includes an authentication secret, creating an external data-transfer path. In the context of an agent skill, that is security-relevant because prompts or generated content may contain sensitive personal, business, or proprietary information that leaves the local boundary.

Content

Scanner excerpt · scripts/make_text_to_video.py (reported line 146)May include surrounding context.

python
"speed": args.speed,
            },
        }
        r = requests.post(
            url,
            headers={"xi-api-key": args.api_key, "Content-Type": "application/json"},
            json=payload,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script transmits arbitrary user-provided text and an API credential to the ElevenLabs external service with no in-band consent, warning, or data-handling disclosure in this file. In a skill context, this matters because users may provide sensitive content expecting local media generation, causing unintended third-party disclosure.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency 'numpy' is unpinned, which makes builds non-reproducible and can cause the environment to resolve to a vulnerable or incompatible release at install time. In a skill that processes media, unexpected dependency drift can introduce exploitable bugs from upstream packages without any code change in the skill itself.

Content

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

text
numpy
pillow
requests

Unverifiable Dependency: numpy has 16 known advisory(ies) (CVE-2014-1859 (Numpy arbitrary file write via symlink attack); CVE-2021-41495 (NumPy NULL Pointer Dereference); CVE-2021-33430 (NumPy Buffer Overflow (Disputed)) +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
89% confidence
Finding

The manifest does not pin a version of 'numpy', so it is impossible to verify whether the installed package is affected by known advisories. While this finding does not prove a vulnerable version will be installed, the lack of version control creates supply-chain uncertainty and can result in deployment of a known-vulnerable release.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency 'pillow' is unpinned, which is risky because image parsing libraries frequently receive security fixes for malformed-file handling, resource exhaustion, and memory safety issues. Since this skill works with avatar frame images, resolving an unsafe Pillow version could directly expose the skill to malicious image inputs.

Content

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

text
numpy
pillow
requests

Unverifiable Dependency: pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +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
95% confidence
Finding

The manifest does not pin a version of 'pillow', and Pillow has a history of image-parsing vulnerabilities including resource exhaustion and, in some releases, more severe memory-safety issues. Because this skill's core function is to process user-supplied or external avatar images, the context makes unverifiable Pillow versions more dangerous than a generic unused dependency.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.