Back to skill

Security audit

media-use

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent media-generation purpose, but it needs Review because it can install unpinned packages, make broad network/media requests, run external tools, and send account-linked telemetry by default.

Install only if you are comfortable with a broad media automation skill that contacts external providers, runs local CLIs, writes reusable media state under the project and ~/.media, and sends opt-out account-linked telemetry. Before use, consider setting HYPERFRAMES_NO_TELEMETRY=1 or DO_NOT_TRACK=1, use --local-only when you do not want network calls, avoid untrusted media URLs, and do not run the BGM generation path unless you accept unpinned Python package installation or have prepared a controlled environment.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lib/local-run.mjs:22
Finding
Shell Command Injection in the Exported Local Model Runner<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/local-run.mjs:22-32, 65` **Vulnerability Type**: Shell command injection **Risk Level**: High ### Vulnerable Code ```js function defaultWhich(bin) { execFileSync("command", ["-v", bin], { stdio: "ignore", shell: true }); } function defaultExec(cmd) { execFileSync(cmd, { stdio: ["ignore", "pipe", "pipe"], shell: true, timeout: 600000 }); } const fill = (tpl, vars) => tpl.replace(/\{(\w+)\}/g, (_, k) => (vars[k] != null ? String(vars[k]) : "")); ``` The generated command is subsequently executed as follows: ```js try { exec(fill(model.invoke, vars)); } catch (e) { lastFailure = { recommend: "install", model: model.id, sizeMB: model.sizeMB, command: model.install, reason: e.message || String(e), }; continue; } ``` Relevant model templates in `scripts/lib/local-models.mjs` contain directly substituted values: ```js invoke: "python -m kokoro --text {text} --voice {voice} --out {out}", ``` ```js invoke: "whisperx {audio} --output_format json --out {out}", ``` ```js invoke: "realesrgan-ncnn-vulkan -i {in} -o {out} -s 4", ``` ### Technical Analysis `fill()` directly inserts values such as speech text, input paths, output paths, and voice names into a command string. `defaultExec()` then executes that string through a system shell by setting `shell: true`. No shell escaping or quoting is applied. Consequently, values containing shell metacharacters such as `;`, `&&`, `|`, backticks, `$()`, redirection operators, or embedded quotes can alter the command structure instead of remaining ordinary arguments. The helper is exported and designed to receive caller-provided `vars`. No active production caller of `runLocalModel()` was identified in the audited repository; the currently active image and video providers use safer argument-array construction. The vulnerability is therefore dormant in the current observed call graph, but it becomes directly exploitable i ...[truncated 1336 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `shell: true` from all model execution. 2. Represent every invocation as a binary and argument array: ```js const [binary, ...argv] = buildArgv(model.invoke, vars); execFileSync(binary, argv, { stdio: ["ignore", "pipe", "pipe"], timeout: 600000, }); ``` 3. Use the existing `buildArgv()` approach from `scripts/lib/local-models.mjs`, which preserves substituted values as individual arguments. 4. Do not attempt to repair this solely through shell escaping; avoiding a shell is safer and less platform-dependent. 5. Validate path variables and constrain enumerated fields such as voice IDs. 6. Add regression tests using values containing: - `; touch /tmp/test` - `$(touch /tmp/test)` - backticks - `&&` and `|` - spaces and quotes 7. Assert that injected metacharacters are passed literally to the model process and never interpreted by a shell. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lib/media-fetch.mjs:38
Finding
DNS-Rebinding SSRF in Remote Media Ingestion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/media-fetch.mjs:38-68`; reachable through `scripts/resolve.mjs:836-863` **Vulnerability Type**: Server-side request forgery through incomplete hostname validation **Risk Level**: High ### Vulnerable Code ```js export function isPublicMediaUrl(value) { try { const url = new URL(value); if (url.protocol !== "http:" && url.protocol !== "https:") return false; const host = url.hostname.replace(/\.$/, ""); if ( host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal") ) return false; const address = host.replace(/^\[|\]$/g, ""); const family = isIP(address); return family === 0 || !blocked.check(address, family === 4 ? "ipv4" : "ipv6"); } catch { return false; } } export async function fetchMedia(url, { method = "GET", signal, fetchImpl = fetch } = {}) { let current = String(url); for (let hop = 0; hop <= 5; hop++) { if (!isPublicMediaUrl(current)) throw new Error("Media download blocked: URL is not public HTTP(S)"); const response = await fetchImpl(current, { method, signal, redirect: "manual" }); if (!(response.status >= 300 && response.status < 400)) return response; const location = response.headers.get("location"); if (!location) return response; await response.body?.cancel(); current = new URL(location, current).href; } throw new Error("Media download exceeded redirect limit"); } ``` The user-controlled URL reaches this downloader through `scripts/resolve.mjs`: ```js const isUrl = /^https?:\/\//i.test(src); if (isUrl && !isDirectMediaUrl(src)) { console.error( `error: --from takes a direct public media URL or a local file; "${src}" is not a direct media link (no platform pages / yt-dlp)`, ); process.exit(2); } ``` ```js async (reservation) => { if (isUrl) await freezeUrl(src, reservation.fullPath); else freezeLo ...[truncated 2552 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve every hostname before connecting using a trusted DNS resolver. 2. Reject the URL if any returned A or AAAA record belongs to a loopback, private, link-local, reserved, multicast, documentation, or otherwise non-public range. 3. Pin the validated address for the actual connection so DNS cannot change between validation and use. 4. Preserve the original hostname for TLS Server Name Indication and certificate verification when using HTTPS. 5. Repeat DNS resolution, address validation, and connection pinning for every redirect target. 6. Consider permitting only HTTPS for remote ingestion. 7. Use explicit hostname allowlists for known provider download domains. 8. Disable proxy inheritance for this request path unless proxies are explicitly trusted. 9. Add tests using a hostname resolver seam that returns: - `127.0.0.1` - `10.0.0.1` - `169.254.169.254` - `::1` - `fc00::1` 10. Add rebinding tests where the first lookup is public and a later lookup becomes private. ]]>

T08 · Insecure Dependencies

Warning
Location
audio/scripts/lib/bgm.mjs:26
Finding
Silent Runtime Installation of Unpinned Python Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `audio/scripts/lib/bgm.mjs:26-48, 139-142` **Vulnerability Type**: Unsafe runtime dependency installation **Risk Level**: Medium ### Vulnerable Code ```js const BGM_PY_DEPS = ["transformers", "torch", "soundfile", "numpy"]; const BGM_PY_PROBE = "import transformers, soundfile, torch, numpy; from transformers import MusicgenForConditionalGeneration"; const LYRIA_PY_DEPS = ["google-genai", "python-dotenv"]; const LYRIA_PY_PROBE = "import google.genai"; function pyOk(probe) { const { cmd, args } = pythonInvocation(["-c", probe]); return spawnSync(cmd, args, { stdio: "ignore" }).status === 0; } function pipInstall(deps) { const { cmd, args } = pythonInvocation(["-m", "pip", "install", "-q", ...deps]); return spawnSync(cmd, args, { stdio: "ignore" }).status === 0; } ``` The installation occurs automatically during BGM generation: ```js if (lyriaConfigured && !pyOk(LYRIA_PY_PROBE)) pipInstall(LYRIA_PY_DEPS); const useLyria = lyriaConfigured && pyOk(LYRIA_PY_PROBE); if (!useLyria && !pyOk(BGM_PY_PROBE)) pipInstall(BGM_PY_DEPS); ``` ### Technical Analysis A BGM generation request can cause the Skill to execute `python -m pip install` without a separate confirmation step. The dependency names are not pinned to exact versions, and no package hashes are verified. The effective code installed can therefore change after the Skill itself has been reviewed. Installation also honors the user’s Python and pip configuration, which may include custom or compromised indexes, dependency mirrors, or environment-specific package precedence. Python package installation can execute build backends and other package-controlled code. The subsequently imported packages and downloaded model tooling also execute with the privileges of the user running the Skill. This behavior conflicts with `references/setup-providers.md`, which describes local tools as opt-in alternatives and provides explicit installation commands. ...[truncated 1267 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic package installation from ordinary media-generation execution. 2. Detect missing dependencies and return an actionable installation command instead. 3. Require explicit user confirmation before any environment modification. 4. Install dependencies into a dedicated virtual environment owned by the Skill rather than the user’s global interpreter. 5. Pin exact package versions and all transitive dependencies. 6. Use a lockfile or requirements file with cryptographic hashes, for example pip’s `--require-hashes`. 7. Configure an approved package index and reject unexpected alternate indexes for automated setup. 8. Display expected download sizes before installing large packages such as PyTorch. 9. Record the environment and installed versions for reproducibility. 10. Keep installation and execution as separate workflow stages so offline/local-only operation cannot unexpectedly initiate dependency downloads. ]]>

other

Warning
Location
scripts/lib/telemetry.mjs:19
Finding
Opt-Out Telemetry Sends HeyGen Account Identity and Stable Installation ID<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/telemetry.mjs:19-20, 132-145, 190-212` **Vulnerability Type**: Account-linked telemetry and privacy exposure **Risk Level**: Medium ### Vulnerable Code ```js const POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx"; const POSTHOG_HOST = "https://us.i.posthog.com"; ``` The implementation reads identity information from the HeyGen credentials file: ```js function heygenAccountDistinctId() { const file = join(process.env.HEYGEN_CONFIG_DIR || join(homedir(), ".heygen"), "credentials"); try { if (!existsSync(file)) return null; const raw = readFileSync(file, "utf8").trim(); if (!raw.startsWith("{")) return null; const parsed = JSON.parse(raw); if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null; const user = parsed.user; if (!user || typeof user !== "object" || Array.isArray(user)) return null; const id = typeof user.email === "string" && user.email.trim() ? user.email : user.username; return typeof id === "string" && id.trim() ? id.trim().toLowerCase() : null; } catch { return null; } } ``` It then links the stable installation identity to that account: ```js async function identifyAccount(anonId) { if (optedOut() || identifiedAccount) return; const distinctId = heygenAccountDistinctId(); if (!distinctId) return; identifiedAccount = true; await postEvent("$identify", { $anon_distinct_id: anonId }, distinctId); } ``` Telemetry is sent by default unless the user opts out: ```js export async function track(event, properties = {}) { if (optedOut()) return; showTelemetryNotice(); const anonId = anonymousId(); await identifyAccount(anonId); await postEvent(event, properties, anonId); } ``` ### Technical Analysis The telemetry subsystem uses a stable UUID stored in `~/.hyperframes/config.json`. When a HeyGen credential file contains a user object, the subsystem reads the account email ...[truncated 2272 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make telemetry opt-in rather than enabled by default. 2. Do not read authentication or credential files for analytics purposes. 3. Remove the `$identify` event and never use email addresses or usernames as telemetry identifiers. 4. Use a random, telemetry-specific identifier that is not shared with authentication or other products. 5. Rotate or expire telemetry identifiers to reduce long-term correlation. 6. Enforce the fixed HTTPS PostHog destination in production; restrict host overrides to test builds or explicit test-only dependency injection. 7. Validate telemetry property names against an allowlist before transmission. 8. Update documentation and notices to describe the data as pseudonymous or account-linked rather than anonymous. 9. Provide a persistent configuration switch in addition to environment-variable opt-outs. 10. Ensure no telemetry is sent before the user has seen and accepted the disclosure. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (174)

Credential Access

High
Category
Privilege Escalation
Content
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
// pipeline. The credential resolver matches the hyperframes CLI auth: first
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
// overrides the dir). Vendored so the skill ships standalone. Pure node.
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
// pipeline. The credential resolver matches the hyperframes CLI auth: first
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
// overrides the dir). Vendored so the skill ships standalone. Pure node.
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
// pipeline. The credential resolver matches the hyperframes CLI auth: first
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
// overrides the dir). Vendored so the skill ships standalone. Pure node.
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
// pipeline. The credential resolver matches the hyperframes CLI auth: first
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
// overrides the dir). Vendored so the skill ships standalone. Pure node.
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
// pipeline. The credential resolver matches the hyperframes CLI auth: first
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
// overrides the dir). Vendored so the skill ships standalone. Pure node.
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
// pipeline. The credential resolver matches the hyperframes CLI auth: first
// usable source wins — $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
// overrides the dir). Vendored so the skill ships standalone. Pure node.
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
export function loadEnvFromDir(startDir) {
  let dir = resolve(startDir);
  for (let i = 0; i < 5; i++) {
    const envPath = join(dir, ".env");
    if (existsSync(envPath)) {
      for (const raw of readFileSync(envPath, "utf8").split("\n")) {
        let line = raw.trim();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
96% confidence
Finding
The image-to-video instructions describe sending any image of a person plus narration to HeyGen without an explicit privacy or consent warning. This is particularly sensitive because it involves biometric/likeness data and voice/script content; misuse could expose personal images to a third party or enable non-consensual avatarization workflows.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
"2130706433",
    "0x7f000001",
    "10.0.0.1",
    "169.254.169.254",
    "100.100.100.200",
    "192.168.1.1",
    "172.31.0.1",
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
"2130706433",
    "0x7f000001",
    "10.0.0.1",
    "169.254.169.254",
    "100.100.100.200",
    "192.168.1.1",
    "172.31.0.1",
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
"2130706433",
    "0x7f000001",
    "10.0.0.1",
    "169.254.169.254",
    "100.100.100.200",
    "192.168.1.1",
    "172.31.0.1",
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
"2130706433",
    "0x7f000001",
    "10.0.0.1",
    "169.254.169.254",
    "100.100.100.200",
    "192.168.1.1",
    "172.31.0.1",
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
"0x7f000001",
    "10.0.0.1",
    "169.254.169.254",
    "100.100.100.200",
    "192.168.1.1",
    "172.31.0.1",
    "224.0.0.1",
Confidence
85% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
}

function restoreEnv(saved) {
  for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
  Object.assign(process.env, saved);
}
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
}

function restoreEnv(saved) {
  for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
  Object.assign(process.env, saved);
}
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
}

function restoreEnv(saved) {
  for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
  Object.assign(process.env, saved);
}
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
}

function restoreEnv(saved) {
  for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
  Object.assign(process.env, saved);
}
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
}

function restoreEnv(saved) {
  for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
  Object.assign(process.env, saved);
}
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
}

function restoreEnv(saved) {
  for (const k of Object.keys(process.env)) if (!(k in saved)) delete process.env[k];
  Object.assign(process.env, saved);
}
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if (ext === ".svg") return { width: null, height: null, duration: null, codec: "svg" };

  try {
    // execFileSync (no shell) so a hostile filename like `"; rm -rf ~; ".png`
    // can't break out of the quoting — filePath is passed as a literal argv entry.
    const raw = execFileSync(
      "ffprobe",
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Chaining Abuse

High
Category
Tool Misuse
Content
if (ext === ".svg") return { width: null, height: null, duration: null, codec: "svg" };

  try {
    // execFileSync (no shell) so a hostile filename like `"; rm -rf ~; ".png`
    // can't break out of the quoting — filePath is passed as a literal argv entry.
    const raw = execFileSync(
      "ffprobe",
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
assert.match(msg, /~87\.5GB/);
  assert.match(msg, /\/home\/tester\/\.cache\/huggingface\/hub/);
  assert.match(msg, /200\.0GB free/);
  assert.equal(/NOT fit/.test(msg), false, "it fits, so no warning");
});

test("describeDownload says plainly when the weights will not fit", () => {
Confidence
80% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This file implements a 'Recipes CLI' for freezing, listing, and reusing HyperFrames user-memory artifacts such as frame specs, storyboard skeletons, and brief values. Those operations are about project recipe/version management rather than resolving, generating, transforming, or analyzing media assets as described in the media-use manifest.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill invokes shell commands (`node ...`, `hyperframes ...`) and depends on environment/provider setup, but it declares no explicit tool scope or permissions. In agent environments, that can cause the skill to be auto-invoked with broader-than-necessary shell access, increasing the chance of unintended command execution or misuse against local files and external provider CLIs.

Vague Triggers

Medium
Confidence
93% confidence
Finding
Telling the agent to use the skill for broad subjective feedback without strong constraints encourages autonomous interpretation and action on ambiguous user intent. In context, this is riskier because the skill can resolve, generate, and operate on media via shell commands and third-party providers, so ambiguity can lead to unnecessary file changes, uploads, or external API use.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
audio/scripts/lib/audio-meta.test.mjs:110

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
audio/scripts/lib/bgm.mjs:38

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
audio/scripts/lib/python.mjs:7

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
audio/scripts/lib/tts.mjs:31

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
audio/scripts/lib/tts.spawn.test.mjs:12

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/dither.test.mjs:24

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/eval.mjs:43

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/codex-provider.mjs:52

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/cube-validate.test.mjs:101

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/cutlist.test.mjs:117

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/duck.test.mjs:100

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/grade-analyzer.mjs:36

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/heygen-cli.mjs:146

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/heygen-cli.test.mjs:250

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/heygen-search.mjs:18

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/local-run.mjs:22

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/npx-sync.mjs:7

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/probe.mjs:11

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/specs.mjs:8

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/resolve.mjs:1121

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/resolve.test.mjs:33

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/transcribe.mjs:110

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/transcript-cut.mjs:178

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
audio/scripts/lib/heygen.mjs:39

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/lib/telemetry.mjs:19