Back to skill

Security audit

Volcengine Video Studio

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned, but its endpoint and download handling are broad enough that users should review it before installing.

Install only if you are comfortable with a skill that submits prompts and selected media to a remote video API and saves outputs locally. Use only trusted HTTPS Volcengine/ARK endpoints, avoid OPENAI_* fallbacks for this skill, pass only media you intend to upload, and consider disabling automatic downloads or choosing a controlled download directory.

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

Error
Location
scripts/generate_video.py:31
Finding

Credentials and User-Selected Local Media Can Be Transmitted to an Untrusted or Plaintext Endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_video.py, lines 31-39, 151-152, and 235-250
Vulnerability Type: Unrestricted sensitive-data transmission destination
Risk Level: High

Vulnerable Code

python
def build_submit_url(base: str) -> str:
    base = base.strip().rstrip("/")
    if not base:
        raise SystemExit("Missing VOLCENGINE_ENDPOINT / ARK_BASE_URL / OPENAI_BASE_URL")
    if base.endswith("/contents/generations/tasks"):
        return base
    if base.endswith("/api/v3"):
        return base + "/contents/generations/tasks"
    return base + "/api/v3/contents/generations/tasks"
python
parser.add_argument("--endpoint", default=getenv("VOLCENGINE_VIDEO_ENDPOINT", "VOLCENGINE_ENDPOINT", "ARK_BASE_URL", "OPENAI_BASE_URL"))
parser.add_argument("--api-key", default=getenv("VOLCENGINE_API_KEY", "ARK_API_KEY", "OPENAI_API_KEY"))
python
def make_headers(api_key: str) -> Dict[str, str]:
    return {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
    }


def submit_task(args: argparse.Namespace, payload: Dict[str, Any]) -> Dict[str, Any]:
    req = urllib.request.Request(
        build_submit_url(args.endpoint),
        data=json.dumps(payload).encode("utf-8"),
        headers=make_headers(args.api_key),
        method="POST",
    )
    return request_json(req, min(args.timeout, 120))

Local files included through --image or --video are encoded and placed in that outbound request:

python
def local_file_to_data_url(path_str: str) -> str:
    path = pathlib.Path(path_str).expanduser().resolve()
    if not path.exists():
        raise SystemExit(f"Local file not found: {path}")
    mime = mimetypes.guess_type(str(path))[0] or "application/octet-stream"
    encoded = base64.b64encode(path.read_bytes()).decode("ascii")
    return f"data:{mime};base64,{encoded}"

Te

...[truncated 2604 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require https:// for all remote API endpoints and reject plaintext HTTP.
  2. Default directly to the documented Volcengine endpoint rather than relying on broad provider fallbacks.
  3. Allow only approved Volcengine or ARK hostnames by default.
  4. If custom compatible endpoints are required, place them behind an explicit option such as --allow-custom-endpoint and present a warning that credentials and media will be transmitted to that host.
  5. Remove the undocumented OPENAI_BASE_URL and OPENAI_API_KEY fallbacks, or require explicit provider selection and validate that the credential source matches the selected endpoint.
  6. Resolve and validate the final destination after redirects and reject redirects to a different or unapproved origin.
  7. Clearly display the destination hostname and the categories of data being uploaded before transmitting local media.
  8. Avoid accepting API keys through command-line arguments where process listings or shell history may expose them; prefer a protected credential store or narrowly scoped environment variable.
  9. Recommend short-lived, least-privilege API credentials with restricted quota.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_video.py:104
Finding

Unvalidated Result URLs Permit SSRF and Unbounded Downloads

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_video.py, lines 104-125 and 261-273
Vulnerability Type: Server-side request forgery and unrestricted remote-file download
Risk Level: High

Vulnerable Code

python
def walk_urls(node: Any, found: List[str]) -> None:
    if isinstance(node, dict):
        for key, value in node.items():
            lowered = str(key).lower()
            if isinstance(value, str) and value.startswith(("http://", "https://")):
                if any(token in lowered for token in ["video", "file", "url", "output", "media", "cover"]):
                    found.append(value)
            else:
                walk_urls(value, found)
    elif isinstance(node, list):
        for item in node:
            walk_urls(item, found)


def download_file(url: str, out_dir: pathlib.Path, filename: str) -> pathlib.Path:
    ensure_dir(out_dir)
    out = out_dir / filename
    with urllib.request.urlopen(url, timeout=600) as resp:
        out.write_bytes(resp.read())
    return out
python
downloads: List[str] = []
if parse_bool(args.download_results) and deduped_urls:
    download_dir = pathlib.Path(args.download_dir).expanduser().resolve()
    stamp = int(time.time())
    stem = safe_stem(prompt_hint or extract_task_id(task_payload) or "video")
    run_dir = download_dir / f"{stamp}-{stem}"
    for idx, url in enumerate(deduped_urls, start=1):
        ext = pathlib.Path(url.split("?", 1)[0]).suffix or ".mp4"
        filename = f"{stamp}-{stem}-{idx}{ext}"
        downloads.append(str(download_file(url, run_dir, filename)))

Technical Analysis

The script recursively treats remote response values as downloadable output URLs when the associated key contains a broad token such as url, file, media, or cover. It then passes each URL directly to urllib.request.urlopen.

The implementation does not validate:

  • Whether the ...[truncated 2389 chars]
Remediation
View remediation

Remediation Suggestions

  1. Permit automatic downloads only from an explicit allowlist of trusted Volcengine or CDN hostnames.
  2. Require HTTPS for result downloads.
  3. Resolve the destination hostname before connecting and reject loopback, private, link-local, multicast, unspecified, reserved, and known cloud metadata address ranges for both IPv4 and IPv6.
  4. Disable redirects or validate the scheme, hostname, port, and resolved address after every redirect.
  5. Restrict destination ports to expected HTTPS ports.
  6. Validate Content-Type against an allowlist of expected video or image media types.
  7. Stream responses in bounded chunks instead of calling resp.read() without a limit.
  8. Enforce a maximum download size using both Content-Length and a running byte counter; abort if the limit is exceeded.
  9. Limit the number of URLs downloaded from one task response.
  10. Prefer extraction from documented, schema-specific result fields rather than recursively accepting URLs based on broad key substrings.
  11. Consider disabling automatic downloads by default and requiring explicit user approval after displaying the validated destination hostname, media type, and expected size.
  12. Save downloads with a controlled extension derived from an approved media type rather than trusting the remote URL path.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding

The skill advertises capabilities that involve environment variable access, reading and writing local files, and making network requests, but it does not declare an explicit permission or allowed-tools scope. That creates an authorization ambiguity: a caller may invoke a skill with broader side effects than expected, including use of API keys and local filesystem writes, without a clear least-privilege boundary.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The notes explicitly recommend preserving the full raw task payload in stdout for debugging, but do not warn that API responses may contain sensitive data such as signed output URLs, callback metadata, request echoes, internal identifiers, or other provider-returned fields. In a production-oriented video generation skill, stdout is often captured by logs, CI systems, agent traces, or shared debugging outputs, which increases the chance of unintended data exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script accepts local paths for --image/--video, converts those files into data URLs, and sends the full contents to the remote API. In a skill whose purpose is media generation, this behavior is expected, but the lack of an explicit runtime warning or confirmation increases the risk of accidental exfiltration of sensitive local files if a user passes the wrong path or does not realize local files are uploaded.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill states that generated videos are downloaded automatically to a local desktop path, but it does not prominently warn about local file creation or storage side effects before doing so. This can lead to unexpected writes of potentially large or sensitive media files to a user workstation, which is especially relevant because the skill is designed to run real jobs and fetch outputs automatically.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

By default, the script downloads all discovered result URLs and writes them to a local directory automatically. In this video-generation context that is functional behavior, but doing so without explicit user confirmation can lead to unintended disk writes, storage consumption, or saving untrusted remote content to a predictable location.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.