Back to skill

Security audit

主图视频 Main Image Video

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its image-to-video purpose, but its brand-file feature can silently add and upload any readable local file named in the brand config.

Install only if you trust the brand files and provider configuration you will use. Before running with --brand, inspect model.reference and any referenced assets, prefer --dry-run to see extra images, avoid third-party brand YAML from untrusted sources, and assume prompts and uploaded images will be sent to the selected cloud provider.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/brand.mjs:49
Finding
Arbitrary Local File Disclosure Through Unvalidated Brand Reference Paths<![CDATA[ ## Vulnerability Details **File Location**: `scripts/brand.mjs:49-51`, `scripts/gen.mjs:129-130`, and provider upload sinks in `scripts/lib/providers.mjs:33-43, 149-151, 203-209, 234-239, 263-268, 298-301` **Vulnerability Type**: Arbitrary local file read and disclosure to an external generation provider **Risk Level**: High ### Vulnerable Code `scripts/brand.mjs:49-51` accepts any existing path from `model.reference`: ```js if (m.reference) { if (existsSync(m.reference)) images.push(m.reference) else parts.push(`(模特参考图 ${m.reference} 找不到,已跳过)`) } ``` `scripts/gen.mjs:129-130` automatically adds that path to the files sent to the selected provider: ```js if (b.append) o.prompt = `${o.prompt}\n${b.append}` if (b.images.length) o.images = [...o.images, ...b.images] ``` For example, `scripts/lib/providers.mjs:33-43` reads local paths without restricting them to image assets: ```js async function asBase64(p) { if (isUrl(p)) { const r = await fetch(p) if (!r.ok) throw new Error(`拉取参考图失败 ${r.status}: ${p}`) return Buffer.from(await r.arrayBuffer()).toString('base64') } return (await readFile(p)).toString('base64') } const asDataUri = async (p) => isUrl(p) ? p : `data:${mimeOf(p)};base64,${await asBase64(p)}` ``` The resulting data is transmitted by provider adapters. For example, the Gemini adapter at `scripts/lib/providers.mjs:203-209` embeds it into an external API request: ```js for (const p of req.images || []) { parts.push({ inline_data: { mime_type: mimeOf(p), data: await asBase64(p) } }) } const j = await postJson( `https://generativelanguage.googleapis.com/v1beta/models/${gemini.model()}:generateContent`, { contents: [{ parts }] }, { 'x-goog-api-key': key }, req.timeoutMs, ) ``` Equivalent local-file upload behavior exists in the OpenAI, fal, Replicate, Ark, and dLazy execution paths. ### Technical Analysis The `--brand` feature is intended to append brand constraints and optionally include a model refe ...[truncated 3338 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Constrain reference paths to an approved directory** - Resolve relative references against the directory containing the brand file. - Canonicalize both the approved root and candidate path with `realpath()`. - Reject candidates whose canonical path is outside the approved root. 2. **Reject dangerous file types** - Use `lstat()` and reject symbolic links, devices, directories, sockets, and other non-regular files. - Permit only an explicit image extension allowlist such as `.jpg`, `.jpeg`, `.png`, and `.webp`. - Validate the file's actual magic bytes rather than trusting its extension. 3. **Apply resource limits** - Enforce a conservative maximum file size before reading or uploading. - Optionally validate image dimensions and decode the image before submission. 4. **Require informed user approval** - Display the canonical path of every automatically added brand asset. - Require explicit confirmation before uploading files not directly supplied through `--images`. - Clearly identify the selected provider and external destination. 5. **Fail closed** - Do not silently skip or accept malformed references. - Reject absolute paths and traversal attempts unless explicitly authorized. - Avoid defaulting unknown files to `image/jpeg`. 6. **Harden all provider adapters** - Centralize local input validation before any provider adapter receives `req.images`. - Ensure the dLazy CLI path receives the same validation as HTTP providers. - Add tests covering absolute paths, `../` traversal, symbolic-link escapes, non-image files, oversized files, and valid in-directory images. A secure resolution pattern should conceptually follow: ```js const brandDir = await realpath(path.dirname(brandFile)) const candidate = await realpath(path.resolve(brandDir, m.reference)) const relative = path.relative(brandDir, candidate) if (relative.startsWith('..') || path.isAbsolute(relative)) { throw ...[truncated 331 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The provider abstraction explicitly supports generic image generation, image editing, and even text outputs across multiple backends, which exceeds the skill’s declared purpose of turning one product image into a short main-image video. In an agent-skill setting, this capability expansion increases the attack surface and creates scope for unintended or policy-bypassing uses if higher-level callers can pass arbitrary requests through this module.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The code accepts arbitrary HTTP(S) URLs and fetches them server-side before forwarding their contents to model providers. In a hosted environment, this can enable SSRF-style access to internal services, metadata endpoints, or otherwise unreachable network locations, which is more dangerous than the skill’s stated static-image-to-video purpose requires.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This module transmits prompts and image contents to multiple third-party services, but the code shown has no in-flow notice, consent check, or provider transparency guard despite handling potentially sensitive commercial media. In this skill context, silent multi-provider egress is risky because users may reasonably expect a narrow local conversion workflow rather than broad external sharing.

External Transmission

Medium
Category
Data Exfiltration
Content
: await readFile(p)
        fd.append('image[]', new Blob([buf], { type: mimeOf(p) }), path.basename(p))
      }
      r = await fetch('https://api.openai.com/v1/images/edits', {
        method: 'POST', headers: { authorization: `Bearer ${key}` }, body: fd,
      })
    } else {
Confidence
93% confidence
Finding
The OpenAI edits request sends prompt and uploaded image content to an external API. That is a real data-egress behavior, and in this skill it matters because product images may contain unreleased assets, branding, or sensitive metadata not clearly disclosed to the user.

External Transmission

Medium
Category
Data Exfiltration
Content
method: 'POST', headers: { authorization: `Bearer ${key}` }, body: fd,
      })
    } else {
      r = await fetch('https://api.openai.com/v1/images/generations', {
        method: 'POST',
        headers: { authorization: `Bearer ${key}`, 'content-type': 'application/json' },
        body: JSON.stringify({
Confidence
91% confidence
Finding
The OpenAI generations request transmits user prompt data to an external service and may return hosted URLs, creating both outbound data exposure and dependency on remote content handling. In a narrowly scoped image-to-video skill, this broad external generation path is more capability than necessary and expands privacy/compliance risk.

External Transmission

Medium
Category
Data Exfiltration
Content
: await readFile(p)
        fd.append('image[]', new Blob([buf], { type: mimeOf(p) }), path.basename(p))
      }
      r = await fetch('https://api.openai.com/v1/images/edits', {
        method: 'POST', headers: { authorization: `Bearer ${key}` }, body: fd,
      })
    } else {
Confidence
93% confidence
Finding
The OpenAI edits request sends prompt and uploaded image content to an external API. That is a real data-egress behavior, and in this skill it matters because product images may contain unreleased assets, branding, or sensitive metadata not clearly disclosed to the user.

External Transmission

Medium
Category
Data Exfiltration
Content
method: 'POST', headers: { authorization: `Bearer ${key}` }, body: fd,
      })
    } else {
      r = await fetch('https://api.openai.com/v1/images/generations', {
        method: 'POST',
        headers: { authorization: `Bearer ${key}`, 'content-type': 'application/json' },
        body: JSON.stringify({
Confidence
91% confidence
Finding
The OpenAI generations request transmits user prompt data to an external service and may return hosted URLs, creating both outbound data exposure and dependency on remote content handling. In a narrowly scoped image-to-video skill, this broad external generation path is more capability than necessary and expands privacy/compliance risk.

External Transmission

Medium
Category
Data Exfiltration
Content
const input = { prompt: req.prompt, num_outputs: req.batch }
    if (req.images?.length) input.input_image = await asDataUri(req.images[0])
    const j = await postJson(
      `https://api.replicate.com/v1/models/${replicate.model(req)}/predictions`,
      { input },
      { authorization: `Bearer ${env.REPLICATE_API_TOKEN}`, prefer: 'wait' },
      req.timeoutMs,
Confidence
90% confidence
Finding
The Replicate prediction request sends prompts and possibly image data to an external provider. In this skill’s business context, that is meaningful data exfiltration outside the platform boundary and should be treated as a real privacy and supply-chain exposure point.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/lib/providers.mjs:104

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/video.mjs:69

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/gen.mjs:118

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/lib/providers.mjs:21