Back to skill

Security audit

transcribe-he

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says: it sends user-provided media to a cloud transcription service and saves transcript files locally, with no hidden persistence or destructive behavior found.

Install only if you are comfortable sending the selected audio/video file or URL, its metadata, and your TextOps API key to the TextOps/cloud transcription service. Avoid using it for highly sensitive recordings unless that service is acceptable for your privacy needs, and remember that both JSON and text transcript files will be left on disk.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (21)

Tainted flow: 'API_KEY' from os.environ.get (line 21, 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 102)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 21, 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 112)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 21, 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 140)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 21, 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 160)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()

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description says the skill should transcribe mp4/mp3/wav/m4a media files using the TextOps/Modal API. The supplied code does something materially different: it converts a JSON transcription result into readable text. Its input is a JSON file, not audio/video media, and there is no transcription engine or external API interaction. While this could be a supporting post-processing step in a transcription pipeline, the chunk itself does not implement the declared primary purpose, so this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs the agent to use shell, network, environment variables, and file writes, but it does not declare any explicit tool scope or permission boundaries. That creates unnecessary execution ambiguity and can let an agent invoke powerful capabilities more broadly than intended when handling user-provided files and URLs.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger language says to always invoke the skill whenever users mention common media extensions or vaguely ask to transcribe 'this,' which can cause unintended activation on ambiguous requests. In a skill that uploads files or fetches URLs to an external API, accidental invocation can lead to unnecessary data transmission and disk writes without sufficiently explicit user intent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The skill sends a job identifier and an API key to an external cloud endpoint, and elsewhere it also directs users to upload or probe user-supplied files/URLs through external services. External transmission is expected for a transcription skill, but it still creates real confidentiality and metadata exposure risk, especially if users provide sensitive audio or private links.

Content

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

To query a job directly (raw API):

bash
curl -X POST https://us-central1-whisper-cloud-functions.cloudfunctions.net/check_modal_job \
  -H "Content-Type: application/json" \
  -H "textops-api-key: $TEXTOPS_API_KEY" \
  -d '{"textopsJobId": "<JOB_ID>"}'

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 57)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
93% confidence
Finding

Remote media URLs provided by the user are sent to an external transcription/probing service without any explicit notice or consent mechanism. This can disclose private resource locations, signed URLs, or access-controlled links to a third party, which is particularly relevant because the skill is designed to automatically trigger on transcription requests.

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/transcribe.py (reported line 102)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
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 112)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()

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Local audio/video files are uploaded to a third-party service with no explicit privacy warning, consent step, or data handling notice. Because transcripts often contain sensitive conversations, this silent upload can expose confidential content beyond the local environment.

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/transcribe.py (reported line 140)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 160)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: 'job_id' from requests.post (line 144, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/transcribe.py (reported line 160)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()

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script always writes both JSON and text outputs, which expands data retention beyond the skill description of producing text. For transcription content, especially sensitive audio, creating an additional structured transcript file can increase accidental exposure and leave artifacts the user did not expect.

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

python
import subprocess
    script_dir = os.path.dirname(os.path.abspath(__file__))
    txt_path = os.path.splitext(output_path)[0] + ".txt"
    result = subprocess.run(
        [sys.executable, os.path.join(script_dir, "json_to_text.py"),
         json_path, "--output", txt_path,
         "--diarization", "true" if has_diarize else "false"],

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The markdown explains that the script automatically saves both .json and .txt outputs next to the original file or in the current directory, but it does not present this as a user warning about modifying local storage. Because the skill affects user data locations by creating files automatically, a clearer disclosure would better satisfy the warning requirement for markdown descriptions.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
71% confidence
Finding

The code uses ffprobe via subprocess and moviepy to inspect local media files before transcription. While this may help estimate polling time, spawning external media-analysis tooling is an additional capability beyond the core stated purpose of sending media for transcription via the TextOps/Modal API.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script writes transcript data to local files automatically without making that persistence obvious to the user. This can leave sensitive transcripts on disk unexpectedly, increasing the chance of later disclosure through backups, shared directories, or other local access.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.