Versioned security assessment

Report ID: SA-B9E5AE9C

7/10/2026, 5:35:06 PM

media-use security assessment v5

Skill Security Certification Report

Audit History
Audit model: codex Historical report
Skill name
media-use
Version
v5
Maintainer
heygen-com
Coverage
103 Files scanned · 13,356 Lines analyzed
Policy version
Unavailable

Highest confirmed finding severity

Critical

17 confirmed security findings require attention.

Installation context

Historical evidence

This report may not describe the currently installable artifact. Open the current Skill page for install guidance.

Open current Skill page

This report does not block or authorize the manifest or ZIP.

The review confirms critical remote curl-to-shell installation instructions and a dangerous mix of command execution, credentials, and network access. Additional risks include shell injection, incomplete SSRF controls, account-linked telemetry, global cross-project data exposure, automatic dependency installation, nested Codex prompting, and unenforced paid-provider consent. Many scanner matches are harmless tests, documentation, template strings, or fixed argv execution, but binary blind spots and capped static coverage still require manual review. Static review was capped at 400/1557 representative findings; omitted static matches are unconfirmed, so automatic publishing stays disabled until manual review.

Report position

Historical report

Open audit history before using this report to install.

Audit attestation

Not attestable

The required immutable binding is incomplete.

Human verification

Not verified

No human verification is recorded for this report.

Coverage

103 Files scanned · 13,356 Lines analyzed

39 items shown for review

Limitations

This report does not claim runtime or sandbox execution and does not prove the absence of side effects.

Evidence chain

Follow the evidence from source binding to the install contract. Available evidence supports verification; it is not a safety guarantee.

  1. Source

    Binding unavailable

  2. Artifact

    Identity incomplete

  3. Audit

    Complete

  4. Install contract

    Open manifest to verify

    Open manifest

Capabilities observed

Observed means this report recorded supporting evidence. Not recorded does not prove that a capability is absent.

Contains scripts

May execute code included with the Skill.

Observed in 17 evidence locations

Network access

May connect to external services.

Observed in 41 evidence locations

Filesystem access

May read or write local files.

Observed in 46 evidence locations

Env variables

May read values from the process environment.

Observed in 42 evidence locations

External commands

May invoke commands or programs outside the Skill.

Observed in 52 evidence locations

Capability review items (22)
High
Hidden file in home directory
// the latter nested it at ~/.media/.media/manifest.jsonl, invisible to the
This module stores complete asset records and files in a global ~/.media cache. The persisted records include prompts and can be reused across unrelated projects.
High
Hidden file in home directory
// project (its own manifest) and across every project (the global ~/.media
Candidate listing reads the global ~/.media manifest and returns descriptions and prompt text from other projects. This can expose client-specific media metadata.
High
Hidden file in home directory
// ~/.hyperframes/config.json, plus a $identify to the HeyGen account on sign-in,
The telemetry module reads and writes a stable identity in ~/.hyperframes/config.json and reads HeyGen credentials to identify signed-in accounts. This is security-sensitive persistent identity access.
High
Hidden file in home directory
// CLI + studio share one install identity in ~/.hyperframes/config.json
The shared hidden configuration file stores the stable identifier and telemetry notice state. Its use enables tracking across tools and sessions.
High
Process exec
exec(fill(model.invoke, vars));
The runner substitutes caller-controlled values into a command string and executes it with shell:true. Shell metacharacters in text, paths, prompts, or output values can cause command injection.
High
Python exec() function
exec(fill(model.invoke, vars));
The call executes a shell command assembled from unescaped template values. The scanner label is imprecise, but the underlying command injection risk is real.
High
Synchronous file execution
execFileSync(
The skill launches an authenticated Codex agent and sends a prompt containing untrusted media intent. The intent is not delimited, and the prompt explicitly suppresses confirmation.
High
Synchronous spawn
return spawnSync(cmd, args, { stdio: "ignore" }).status === 0;
This call runs python -m pip install automatically when dependencies are missing. The packages are unpinned and installation occurs without explicit approval.
High
Fetch API call
const res = await fetch(url);
The fetched URL can come from the user-provided --from option. Validation checks only the literal hostname, so DNS rebinding or a redirect can reach private services.
Medium
Hidden file access
// the latter nested it at ~/.media/.media/manifest.jsonl, invisible to the
The code accesses the global ~/.media manifest and cache, which persists project asset metadata outside the current project. This creates a cross-project confidentiality boundary.
Medium
Hidden file access
// project (its own manifest) and across every project (the global ~/.media
The provider intentionally reads global cache records and exposes their descriptions and prompts to the current agent. The access is functional, not a harmless text match.
Medium
Fetch API call
await fetch(`${POSTHOG_HOST}/batch/`, {
This request sends usage events to PostHog by default. Signed-in users are linked through an email or username identifier unless they opt out.
Low
Environment variable access (bracket notation)
if (!(key in process.env)) process.env[key] = val;
The loader writes every parsed .env key into process.env. This broad import exceeds the specific HeyGen keys required by the feature.
Low
Environment variable access (dot notation)
const lyriaKey = () => process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || "";
The generator reads cloud API keys from process.env. They are used for the intended provider, but this remains credential access.
Low
Environment variable access (dot notation)
const envKey = process.env.HEYGEN_API_KEY || process.env.HYPERFRAMES_API_KEY;
The code retrieves live HeyGen or HyperFrames API keys from process.env for authenticated API calls. This is expected but security-sensitive.
Low
Environment variable access (dot notation)
const file = join(process.env.HEYGEN_CONFIG_DIR || join(homedir(), ".heygen"), "credentials");
The code uses an environment-controlled configuration directory to locate the HeyGen credential file. This is legitimate but accesses sensitive authentication state.
Low
Environment variable object
const lyriaKey = () => process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || "";
The environment object is used to retrieve Gemini or Google API keys for cloud music generation. The credential access is real and should stay visible.
Low
Environment variable object
if (!(key in process.env)) process.env[key] = val;
The process environment receives all keys parsed from the discovered .env file. Child tools can inherit those values.
Low
Environment variable object
const envKey = process.env.HEYGEN_API_KEY || process.env.HYPERFRAMES_API_KEY;
The environment object is used to access live HeyGen credentials. No direct exfiltration beyond intended HeyGen requests was found.
Low
Environment variable object
const file = join(process.env.HEYGEN_CONFIG_DIR || join(homedir(), ".heygen"), "credentials");
The environment selects the directory containing HeyGen credentials. The behavior is intentional but security-sensitive.
Low
Python environment access
client = ElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"])
The generated Python helper reads ELEVENLABS_API_KEY from the inherited environment. It uses the key for the intended TTS provider, but the secret is exposed to a child process.
Low
Python environment access
api_key = os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY") or ""
The Lyria helper reads Google or Gemini API keys from its environment. This is expected provider authentication, but it is live credential handling.

Risk findings

Confirmed security concerns are separated from items that still need review.

Confirmed security concerns (17)

RISK-001 Critical
Pipe to shell pattern
"curl -fsSL https://static.heygen.ai/cli/install.sh | bash && heygen auth login --oauth";
The exported installation command downloads a remote script and pipes it directly to bash without pinning or verification. A compromised host or script can execute arbitrary code.
RISK-002 Critical
Pipe to shell pattern
curl -fsSL https://static.heygen.ai/cli/install.sh | bash
The setup instructions tell users to execute a network response directly with bash. There is no checksum, signature, version pin, or inspection step.
RISK-003 Critical
Pipe to shell pattern
| `heygen` | catalog (bgm/sfx/image/icon) + TTS (voice) + avatar video — the free-usage pa
The tool table repeats the remote curl-to-bash installation command. This creates the same unaudited remote code execution risk as the primary setup instructions.
RISK-004 Critical
[HEURISTIC] DANGEROUS COMBINATION: Code execution + Network + Credential access
This combination is common in credential stealers and RATs
The combination is present: the skill executes local tools, reads authentication state, and makes network requests. The reviewed code contains concrete shell, telemetry, and nested-agent risks even though the media workflow is legitimate.
RISK-005 High
Environment file access
const lyriaKey = () => process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || "";
The generator reads Gemini or Google API keys from the environment to enable Lyria. The use is expected, but the code handles live secrets.
RISK-006 High
Environment file access
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
The credential resolver reads nearby .env files and ~/.heygen/credentials. This is intentional authentication behavior, but it accesses sensitive files and broad project secrets.
RISK-007 High
Environment file access
// Walk up ≤5 dirs from startDir; load the first .env (shell env always wins).
The function walks up five directories looking for a .env file. This can import secrets from parent projects or workspace roots beyond the requested media project.
RISK-008 High
Environment file access
const envPath = join(dir, ".env");
The code locates and reads a project or parent .env file. It loads every parsed key into process.env rather than selecting only required provider keys.
RISK-009 High
Environment file access
if (!(key in process.env)) process.env[key] = val;
All parsed .env keys are copied into the process environment. Later child processes inherit that environment, which can expose unrelated project secrets to external tools.
RISK-010 High
Environment file access
const envKey = process.env.HEYGEN_API_KEY || process.env.HYPERFRAMES_API_KEY;
The code reads HeyGen API credentials from the process environment for authenticated requests. This is expected functionality, but it is real credential access.
RISK-011 High
[HEURISTIC] SUSPICIOUS COMBINATION: Filesystem + Credentials + Network
This combination could indicate credential harvesting and exfiltration
The skill reads credential and identity files, persists cross-project data, and sends telemetry over the network. These actions are documented, but their combination creates real privacy and credential-handling exposure.
RISK-012 High
Global Cache Exposes Assets and Prompts Across Projects
Resolved files and complete manifest records are copied into a global cache. Candidate listing returns descriptions and prompt text from unrelated projects, including other client work.
The documentation explicitly warns that another client's brand mark and prompt can surface, and the code stores and returns those fields.
RISK-013 High
Untrusted Intent Is Delegated to an Authenticated Codex Agent
Media intent is concatenated directly into a Codex execution prompt without delimiters. On image fallback, the nested agent runs automatically and is instructed not to request confirmation.
The code directly embeds intent before imperative instructions and launches codex exec with authenticated image tooling during the normal fallback cascade.
RISK-014 High
Unpinned Python Dependencies Install Without Approval
Missing BGM dependencies trigger python -m pip install automatically. Package versions are not pinned, and installation occurs before local music generation without user confirmation.
The implementation explicitly invokes pip install for unversioned dependency arrays when import probes fail.
RISK-015 High
Direct URL SSRF Guard Can Be Bypassed
The direct URL validator blocks only literal private hostnames. DNS rebinding, private DNS resolution, and redirects can bypass the check before the URL is fetched.
The source comment acknowledges the DNS resolution gap, and fetch follows validation without resolving or rechecking the destination.
RISK-016 High
Paid Provider Confirmation Is Not Enforced
HeyGen TTS is marked as potentially paid, but provider dispatch ignores the paid flag. A normal voice request can invoke billable service after free quota is exhausted.
The registry documents confirmation as required and marks HeyGen TTS paid, while runProviders invokes the capability without checking consent.
RISK-017 Medium
Account-Linked Telemetry Sends Personal Identifiers
Telemetry reads the signed-in HeyGen email or username and sends it to PostHog as a distinct identifier. Collection is enabled by default and requires an environment opt-out.
The code directly reads user.email or user.username from the credential file and posts an identify event to PostHog.

Remediation

Suggested fixes recorded by this audit. Applying them is the maintainer’s responsibility.

  1. FIX-001
    Critical
    Remote installer is piped directly into bash.
    Publish versioned artifacts with checksums or signatures. Download, verify, and execute a pinned installer as separate steps.
  2. FIX-002
    High
    Local model commands use shell:true with interpolated values.
    Tokenize fixed command templates and call execFile or spawn with an argv array. Validate every path, prompt, and numeric parameter.
  3. FIX-003
    High
    Direct URL validation checks only literal hostnames.
    Resolve DNS before connection, reject private and metadata addresses, disable or validate redirects, and recheck every resolved destination.
  4. FIX-004
    High
    Telemetry links events to email or username by default.
    Require explicit consent before account identification. Use a random identifier by default and document retention, destination, and deletion controls.
  5. FIX-005
    High
    Global caching stores project assets and prompt metadata automatically.
    Make cross-project promotion opt-in, isolate caches by workspace, redact prompt and client metadata, and add cache inspection and deletion commands.
  6. FIX-006
    High
    Untrusted intent is forwarded to an authenticated Codex sub-agent.
    Use strict prompt delimiters, validate intent, restrict the subprocess environment and permissions, and require explicit provider approval before execution.
  7. FIX-007
    High
    BGM generation installs unpinned Python packages automatically.
    Remove runtime installation. Provide a locked dependency file, require explicit setup, and verify package hashes in an isolated environment.
  8. FIX-008
    High
    The paid provider flag is informational only.
    Enforce a confirmation token or explicit provider selection before any provider marked paid can run.
  9. FIX-009
    Medium
    Static review capped
    Manually review the omitted 1157 static analyzer matches or reduce bundled generated/vendor/reference content before enabling automatic publication.
  10. FIX-010
    Medium
    Binary assets and lower-priority static matches remain unreviewed.
    Inspect every binary asset, verify provenance and hashes, and complete review of the 1,157 static matches omitted by the reporting cap.

Expert evidence

Immutable subject identity, scanner metadata, dismissed matches, and source-level evidence.

Artifact subject

Marketplace commit
Unavailable
Content hash
Unavailable
Tree hash
Unavailable
Skill path
Unavailable
Audit payload hash
Unavailable

Analysis metadata

Audit model: codex

Analysis state: Complete

Scope is limited to the recorded files, lines, methods, and evidence. No runtime or sandbox execution is claimed.

Verify and export

The manifest and lockfile bind install artifacts to cryptographic hashes. This integrity claim is separate from the security assessment.

Audit attestation: not_attestable