Back to skill

Security audit

transcription-speech-to-text-hebrew

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real cloud transcription tool, but it needs Review because it uploads media and an API key externally, auto-installs/upgrades yt-dlp, and overclaims generic assistant capability questions.

Install only if you are comfortable sending selected media, source URLs, filenames, job IDs, and TEXTOPS_API_KEY to TextOps-controlled endpoints. Avoid sensitive recordings until the publisher fixes endpoint disclosure, validates signed upload hosts, narrows activation scope, and removes automatic unpinned yt-dlp installation or moves it to an explicit isolated setup step.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:3
Finding

Generic Capability Queries Are Hijacked by the Skill

Content
View full analysis
**TextOps Transcription Skill — מה אני יכול לעשות:** > - תמלול קבצי אודיו/וידאו (mp3, mp4, wav, m4a, ועוד) > - תמלול מ-YouTube (הורדה אוטומטית) > - זיהוי דוברים אוטומטי (עד 5 דוברים) > - timestamps ברמת מילה > - שמירת תוצאות כ-.txt וכ-.json > - המרת JSON קיים ל-text Do not proceed to any transcription steps — just answer and stop. ``` ### Technical Analysis The Skill declares that it must activate for generic questions such as “what can you do?” and “any similar capability question.” These requests do not necessarily concern transcription or this specific Skill. Once activated, the Skill requires the Agent to produce a predefined, branded TextOps response and then stop. This changes the Agent’s current response objective and suppresses the normal handling of a broad class of unrelated requests. The activation scope therefore exceeds the minimum privileges and routing behavior required to provide speech-to-text functionality. The problematic behavior is contained in the Skill instructions rather than in an executable script. ...[truncated 1208 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/download_audio.py:49
Finding

Automatic Installation and Upgrade of an Unpinned Dependency

Content
View full analysis
Remediation
View remediation
--hash=sha256: ``` 4. Install dependencies into a dedicated virtual environment rather than the active or system-managed Python environment. 5. Remove `--break-system-packages`. 6. Do not automatically upgrade after every download failure. Diagnose and report the original error instead. 7. If self-installation is operationally unavoidable: - Restrict installation to an isolated environment. - Use a trusted, explicitly configured package index. - Require hashes. - Verify package signatures or provenance where supported. - Ask for explicit user consent. 8. Execute the dependency with a minimal environment and only the filesystem/network access needed for the requested download. ]]>

other

Warning
Location
scripts/transcribe.py:28
Finding

Actual Credential and Data Recipient Differs from Privacy Disclosure

Content
View full analysis
**Publisher** > This skill is published by TextOps (https://agents.text-ops-subs.com). For questions about the service, data handling, or anything else, use the contact form on the website. > **Data & Privacy** > This skill sends data to TextOps servers (`agents.text-ops-subs.com`): > - **Audio/video file** — uploaded for transcription, then deleted from the server after processing > - **TEXTOPS_API_KEY** — sent in request headers to authenticate each call ``` The executable code instead sends requests to the apex `text-ops-subs.com` host: ```python GET_UPLOAD_URL = "https://text-ops-subs.com/api/v2/upload-url" SUBMIT_MODAL_URL = "https://text-ops-subs.com/api/v2/transcribe" CHECK_JOB_URL = "https://text-ops-subs.com/api/v2/transcribe-status" PROBE_URL = "https://text-ops-subs.com/api/v2/probe" ``` The API key is sent to that host during URL probing and signed-URL acquisition: ```python def probe_url(url): """Check accessibility and metadata of a remote audio/video URL.""" res = requests.post(PROBE_URL, json={"url": url}, headers={"textops-api-key": API_KEY}) res.raise_for_status() return res.json() def get_signed_urls(filename): log(f"[UPLOAD] Getting signed URL for: {filename}") res = requests.post(GET_UPLOAD_URL, json={"filename": filename}, headers={"textops-api-key": API_KEY}) res.raise_for_status() return res.json() ``` Transcription submission also sends the credential to the apex host: ```python def submit_job(download_url, has_diarization, word_timestamps=False, min_sp ...[truncated 3565 chars]
Remediation
View remediation
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (27)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/transcribe.py (reported line 106)May include surrounding context.

python
def probe_url(url):
    """Check accessibility and metadata of a remote audio/video URL."""
    res = requests.post(PROBE_URL, json={"url": url},
                        headers={"textops-api-key": API_KEY})
    res.raise_for_status()
    return res.json()

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/transcribe.py (reported line 116)May include surrounding context.

python
def get_signed_urls(filename):
    log(f"[UPLOAD] Getting signed URL for: {filename}")
    res = requests.post(GET_UPLOAD_URL, json={"filename": filename},
                        headers={"textops-api-key": API_KEY})
    res.raise_for_status()
    return res.json()

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/transcribe.py (reported line 144)May include surrounding context.

python
"word_timestamps": word_timestamps,
    }
    log("[JOB] Submitting...")
    res = requests.post(SUBMIT_MODAL_URL,
                        json={"download_url": download_url, "params": params},
                        headers={"textops-api-key": API_KEY})
    res.raise_for_status()

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/transcribe.py (reported line 164)May include surrounding context.

python
last_progress = -1
    for attempt in range(1, max_polls + 1):
        res = requests.post(CHECK_JOB_URL,
                            json={"textopsJobId": job_id},
                            headers={"textops-api-key": API_KEY})
        res.raise_for_status()

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/transcribe.py (reported line 325)May include surrounding context.

python
if args.job_id:
        if args.check_once:
            # Single poll — no sleep, exit immediately with status code
            res = requests.post(CHECK_JOB_URL,
                                json={"textopsJobId": args.job_id},
                                headers={"textops-api-key": API_KEY})
            res.raise_for_status()

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The documented purpose says the skill transcribes media via the TextOps API, but the behavior also includes downloading YouTube audio and installing/upgrading yt-dlp. That hidden expansion of behavior is dangerous because users and reviewers may consent to transcription without realizing the skill will fetch third-party content and execute package-management-related actions on the host.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented purpose says the skill transcribes media via the TextOps API, but the behavior also includes downloading YouTube audio and installing/upgrading yt-dlp. That hidden expansion of behavior is dangerous because users and reviewers may consent to transcription without realizing the skill will fetch third-party content and execute package-management-related actions on the host.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The trigger guidance uses ambiguous catch-all language like 'any similar capability question' without clear exclusion boundaries. Ambiguous routing is risky because it increases accidental activation of a skill that can access files, environment secrets, the network, and the shell.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill instructs the agent to use shell, filesystem, environment-variable access, and network operations, but it does not declare any explicit tool scope or allowed-tools boundary. That makes the runtime privileges implicit and broader than necessary, increasing the chance of unintended command execution, data access, or outbound transmission if the skill is triggered unexpectedly or modified later.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger guidance uses ambiguous catch-all language like 'any similar capability question' without clear exclusion boundaries. Ambiguous routing is risky because it increases accidental activation of a skill that can access files, environment secrets, the network, and the shell.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

The metadata sets language: "he", and much of the prescribed user-facing messaging is fixed in Hebrew. The file does not clearly offer a language choice or explain a justified region-specific limitation, which can violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The skill transmits data and an API credential to an external service endpoint, which is a genuine external-transmission risk even if it is core to the product's function. The context makes this less suspicious because the skill openly discloses the transmission and the service purpose is transcription, but it still creates confidentiality and secret-handling exposure if invoked on sensitive files.

Content

Scanner excerpt · SKILL.md (reported line 269)May include surrounding context.

To query a job directly (raw API):

bash
curl -X POST https://agents.text-ops-subs.com/api/v2/transcribe-status \
  -H "Content-Type: application/json" \
  -H "textops-api-key: $TEXTOPS_API_KEY" \
  -d '{"textopsJobId": "<JOB_ID>"}'

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill metadata says it transcribes user-provided audio/video through a transcription API, but this script adds a separate capability to fetch media from arbitrary YouTube URLs. That scope expansion increases attack surface, may enable unreviewed external network access and content retrieval, and can violate user or platform expectations about what the skill is allowed to do.

Content

No source excerpt is available for this finding.

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/download_audio.py (reported line 42)May include surrounding context.

python
def _yt_dlp_version():
    """Return yt-dlp version string, or None if not available."""
    try:
        result = subprocess.run(
            ["yt-dlp", "--version"],
            capture_output=True, text=True
        )

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Installing or upgrading yt-dlp at runtime lets the skill modify the host Python environment and pull executable code from package infrastructure during normal operation. In a transcription skill, that is an unnecessary privilege expansion that can undermine reproducibility, break the environment, or introduce supply-chain risk if package sources or dependency resolution are compromised.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script silently performs pip installation with --break-system-packages and without clear user or administrator approval, which can alter or damage the local Python installation. Even if not malicious, this behavior is unsafe because it makes persistent host changes from a routine content-processing workflow and hides that side effect behind normal skill execution.

Content

No source excerpt is available for this finding.

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/download_audio.py (reported line 59)May include surrounding context.

python
"--break-system-packages", "-q",
           *(["--upgrade"] if upgrade else []),
           "yt-dlp"]
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode == 0

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/download_audio.py (reported line 107)May include surrounding context.

python
"--break-system-packages", "-q",
           *(["--upgrade"] if upgrade else []),
           "yt-dlp"]
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode == 0

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/transcribe.py (reported line 61)May include surrounding context.

python
def _try_ffprobe(file_path):
    import subprocess
    result = subprocess.run(
        ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_streams", file_path],
        capture_output=True, text=True, timeout=10
    )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill sends user-provided remote media URLs and, elsewhere in the script, uploads local files to a third-party transcription service without any in-script consent gate, disclosure prompt, or data-classification warning. In an agent skill context this is more dangerous because users may believe processing is local while sensitive recordings, meetings, or personal data are transferred off-platform.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This code transmits a user-supplied media URL to an external service for probing, which discloses the target resource and associated metadata to a third party. In a transcription skill, that can expose sensitive file locations, signed URLs, or private cloud-share links if the user is not clearly informed.

Content

Scanner excerpt · scripts/transcribe.py (reported line 106)May include surrounding context.

python
def probe_url(url):
    """Check accessibility and metadata of a remote audio/video URL."""
    res = requests.post(PROBE_URL, json={"url": url},
                        headers={"textops-api-key": API_KEY})
    res.raise_for_status()
    return res.json()

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

Requesting an upload URL from the external provider is part of transferring user content to a third-party processor. While expected for service operation, it is still a privacy-relevant external transmission in this skill because it prepares off-platform upload of potentially sensitive media.

Content

Scanner excerpt · scripts/transcribe.py (reported line 116)May include surrounding context.

python
def get_signed_urls(filename):
    log(f"[UPLOAD] Getting signed URL for: {filename}")
    res = requests.post(GET_UPLOAD_URL, json={"filename": filename},
                        headers={"textops-api-key": API_KEY})
    res.raise_for_status()
    return res.json()

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

Submitting the download URL and transcription parameters to the remote transcription service initiates processing of user media by a third party. In this skill's context, that is the core behavior, but it remains a real data-exposure concern if the user is not clearly notified and allowed to opt in.

Content

Scanner excerpt · scripts/transcribe.py (reported line 144)May include surrounding context.

python
"word_timestamps": word_timestamps,
    }
    log("[JOB] Submitting...")
    res = requests.post(SUBMIT_MODAL_URL,
                        json={"download_url": download_url, "params": params},
                        headers={"textops-api-key": API_KEY})
    res.raise_for_status()

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/transcribe.py (reported line 164)May include surrounding context.

python
last_progress = -1
    for attempt in range(1, max_polls + 1):
        res = requests.post(CHECK_JOB_URL,
                            json={"textopsJobId": job_id},
                            headers={"textops-api-key": API_KEY})
        res.raise_for_status()

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/transcribe.py (reported line 325)May include surrounding context.

python
last_progress = -1
    for attempt in range(1, max_polls + 1):
        res = requests.post(CHECK_JOB_URL,
                            json={"textopsJobId": job_id},
                            headers={"textops-api-key": API_KEY})
        res.raise_for_status()

Static analysis

No suspicious patterns detected.