Back to skill

Security audit

Create Video

Security checks for vulnerabilities and agentic risk

Overview

Review recommended: the skill mostly matches HeyGen video creation, but it grants broad HeyGen access including deletion and includes under-scoped network upload examples.

Install only if you are comfortable giving the agent access to your HeyGen account through HEYGEN_API_KEY. Use a limited or dedicated key if possible, require explicit confirmation before any deletion or media upload, avoid private/internal URLs in upload-from-URL flows, and harden any webhook handler before deploying it publicly.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
references/assets.md:180
Finding

Arbitrary HTTPS Asset Retrieval Enables SSRF and Unintended Data Exfiltration

Content
View full analysis
{ // 1. Validate and download the file const url = new URL(sourceUrl); if (url.protocol !== "https:") { throw new Error("Only HTTPS URLs are supported"); } const sourceResponse = await fetch(sourceUrl); const buffer = Buffer.from(await sourceResponse.arrayBuffer()); // 2. Upload directly to HeyGen const response = await fetch("https://upload.heygen.com/v1/asset", { method: "POST", headers: { "X-Api-Key": process.env.HEYGEN_API_KEY!, "Content-Type": contentType, }, body: buffer, }); const json: AssetUploadResponse = await response.json(); if (json.code !== 100) { throw new Error(json.message ?? "Upload failed"); } return json.data; } ``` ### Technical Analysis The function accepts an externally supplied `sourceUrl` and validates only that its parsed scheme is HTTPS. This validation does not prevent requests to: - Loopback addresses such as `127.0.0.1` or `::1` - RFC 1918 private networks - Link-local and cloud metadata addresses - Internal services exposed through private DNS - Public URLs that redirect to internal destinations - DNS names that resolve differently between validation and connection After retrieving the target, the function buffers the entire response in memory and uploads it to HeyGen. Consequently, this is not only an SSRF primitive but also a potential data-exfiltration path from an internal service to an external third party. The implementation also has no response-size limit, download timeout, content validation, or redirect policy. A large or indefinitely streaming response could therefore cause excessive memory consumption or t ...[truncated 1155 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
references/webhooks.md:36
Finding

Primary Webhook Examples Process Events Without Authentication

Content
View full analysis
{ // Acknowledge receipt immediately res.status(200).send("OK"); // Process event asynchronously processWebhookEvent(req.body).catch(console.error); }); ``` ```python @app.route("/webhook/heygen", methods=["POST"]) def heygen_webhook(): event = request.json # Acknowledge immediately response = jsonify({"status": "received"}) # Process asynchronously thread = threading.Thread( target=process_webhook_event, args=(event,) ) thread.start() return response, 200 ``` ### Technical Analysis The primary Express and Flask setup examples accept arbitrary POST bodies and dispatch them for asynchronous processing without first authenticating the sender. An Internet-accessible attacker can therefore submit events that appear to originate from HeyGen. Although the document later presents signature-verification guidance, it is conditional and is not integrated into the initial copy-ready handlers. Implementations based on those handlers are insecure by default. The later TypeScript verification example also derives the signed payload using `JSON.stringify(req.body)`. Cryptographic webhook signatures normally need to be calculated over the exact raw bytes received. Parsing and serializing JSON can change whitespace, escaping, or key representation, causing valid signatures to fail or producing verification behavior inconsistent with the provider's signing protocol. ### Attack Path 1. An application deploys one of the documented webhook handlers on a public endpoint. 2. An attacker discovers or predicts the endpoint path. 3. The attacker submits a forged payload containing a recognized event such as `avatar_video.success` o ...[truncated 973 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (65)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Confidence
85% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/webhooks.md (reported line 186)May include surrounding context.

} }

text

## Registering a Webhook URL

Configure your webhook URL through the HeyGen dashboard or API:

### Request Fields

| Field | Type | Req | Description |
|-------|------|:---:|-------------|
| `url` | string | ✓ | Your webhook endpoint URL |
| `events` | array | ✓ | Event types to subscribe to |
| `secret` | string | | Shared secret for signature verification |

### Via API

```bash
curl -X POST "https://api.heygen.com/v1/webhook/endpoint.add" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-domain.com/webhook/heygen",
    "events": ["avatar_video.success", "avatar_video.fail"]
  }'

TypeScript

typescript
interface WebhookConfig {
  url: string;                                 // Required
  events: string[];                            // Required
  secret?: string;
}

async function registerWebhook(config: WebhookConfig): Promise<void> {
  const response = await fetch("https://api.heygen.com/v1/webh

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

Exposing a deletion tool/path without parameter constraints or confirmation creates a real tool-abuse risk. An agent could be induced to pass arbitrary video_id values—potentially after enumerating videos via the list endpoint—and delete assets the user did not intend to remove, causing irreversible loss of content.

Content

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

md
| Generate video from prompt | `mcp__heygen__generate_video_agent` | `POST /v3/video-agents` |
| Check video status / get URL | `mcp__heygen__get_video` | `GET /v3/videos/{video_id}` |
| List account videos | `mcp__heygen__list_videos` | `GET /v3/videos` |
| Delete a video | `mcp__heygen__delete_video` | `DELETE /v3/videos/{video_id}` |

If no HeyGen MCP tools are available, use direct HTTP API calls as documented in the reference files.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · references/prompt-examples.md (reported line 21)May include surrounding context.

Tone: Confident CEO, data-backed

text

### Output Prompt

FORMAT: Bloomberg-style company report. 90 seconds. Fast-paced, data-dense.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The description says to use this skill when the user says "make me a video" or "create a video about X," which are broad, everyday phrases that could match many unrelated contexts. The file does not provide exclusion conditions or tighter trigger boundaries beyond preferring another skill for precise control.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · SKILL.md (reported line 24)May include surrounding context.

All requests require the X-Api-Key header. Set the HEYGEN_API_KEY environment variable.

bash
curl -X POST "https://api.heygen.com/v3/video-agents" \
  -H "X-Api-Key: $HEYGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Create a 60-second product demo video."}'

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The skill advertises delete capability without any confirmation, authorization, or user-warning language around destructive actions. In an agent context, that increases the risk of accidental or prompt-induced deletion of account videos, especially because the same skill also supports listing videos, which can enable easy target enumeration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The document instructs users to upload raw local images, videos, and audio to a third-party API without clearly warning that local files will be transmitted off-system and become remotely accessible via returned asset URLs. In an agent skill, omission of this disclosure increases the chance that sensitive local media is sent externally without informed consent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/assets.md (reported line 41)May include surrounding context.

| data.meta | string | null | Asset metadata | | data.created_ts | number | Unix timestamp of creation |

curl

bash
curl -X POST "https://upload.heygen.com/v1/asset" \

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/assets.md (reported line 76)May include surrounding context.

md
const resolvedPath = path.resolve(filePath);
  const fileBuffer = fs.readFileSync(resolvedPath);

  const response = await fetch("https://upload.heygen.com/v1/asset", {
    method: "POST",
    headers: {
      "X-Api-Key": process.env.HEYGEN_API_KEY!,

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/assets.md (reported line 112)May include surrounding context.

md
const resolvedPath = path.resolve(filePath);
  const fileBuffer = fs.readFileSync(resolvedPath);

  const response = await fetch("https://upload.heygen.com/v1/asset", {
    method: "POST",
    headers: {
      "X-Api-Key": process.env.HEYGEN_API_KEY!,

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/assets.md (reported line 142)May include surrounding context.

md
def upload_asset(file_path: str, content_type: str) -> dict:
    with open(file_path, "rb") as f:
        response = requests.post(
            "https://upload.heygen.com/v1/asset",
            headers={
                "X-Api-Key": os.environ["HEYGEN_API_KEY"],

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/video-agent.md (reported line 214)May include surrounding context.

md
def upload_asset(file_path: str, content_type: str) -> dict:
    with open(file_path, "rb") as f:
        response = requests.post(
            "https://upload.heygen.com/v1/asset",
            headers={
                "X-Api-Key": os.environ["HEYGEN_API_KEY"],

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The reference adds a convenience function that fetches arbitrary remote URLs and then re-uploads the content to HeyGen. In an agent/tooling context, this expands the capability from simple local asset upload to network retrieval of attacker-controlled URLs, which can enable SSRF-like behavior, retrieval of sensitive intranet resources, or unreviewed third-party content exfiltration if callers pass untrusted URLs.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

Unlike the other upload examples, this transmission is preceded by fetching an arbitrary source URL and then relaying the content to HeyGen. In an agent context, that creates a network pivot and can be abused to fetch attacker-supplied or internal-only resources, making the external transmission materially riskier than a straightforward user-local upload.

Content

Scanner excerpt · references/assets.md (reported line 190)May include surrounding context.

md
const buffer = Buffer.from(await sourceResponse.arrayBuffer());

  // 2. Upload directly to HeyGen
  const response = await fetch("https://upload.heygen.com/v1/asset", {
    method: "POST",
    headers: {
      "X-Api-Key": process.env.HEYGEN_API_KEY!,

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The function name and comments indicate a workflow that uploads a background and then creates a video using that uploaded background, but the actual request body ignores the constructed config object and instead posts a prompt with logoAssetId and bgAssetId, which are different and even undefined in the snippet. This is an active contradiction between the inline intent/documentation and the code shown.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/assets.md (reported line 312)May include surrounding context.

md
// 3. Generate video with Video Agent
  console.log("Generating video...");
  const response = await fetch("https://api.heygen.com/v3/video-agents", {
    method: "POST",
    headers: {
      "X-Api-Key": process.env.HEYGEN_API_KEY!,

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/dimensions.md (reported line 66)May include surrounding context.

};

text

### curl

```bash
# Landscape 1080p

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 24)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/assets.md (reported line 312)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/dimensions.md (reported line 70)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/quota.md (reported line 15)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/quota.md (reported line 30)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/quota.md (reported line 45)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/quota.md (reported line 85)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/quota.md (reported line 113)May include surrounding context.

curl

bash
curl -X GET "https://api.heygen.com/v2/user/remaining_quota" \
  -H "X-Api-Key: $HEYGEN_API_KEY"

Static analysis

No suspicious patterns detected.