Back to skill

Security audit

Video2text Ai 1.0.1

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to implement its advertised cloud video transcription workflow, but its file and URL handling can send broader local or internal data to remote services than the documentation clearly scopes.

Install only if you are comfortable sending selected media, prompts, task IDs, and some local path metadata to this third-party service. Use it only with non-sensitive videos from explicit paths or public URLs you trust; avoid private files, internal URLs, localhost URLs, cloud metadata addresses, and regulated or confidential recordings unless you have verified the provider's retention and data-handling practices.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils/validator.js:12
Finding
Arbitrary Local Files Can Be Uploaded to an External Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:12-19`; upload flow in `scripts/video2text/index.js:128-149` **Vulnerability Type**: Unrestricted local file selection and external upload **Risk Level**: High ### Code Snippet ```js function isFilePath(path) { try { const stats = fs.statSync(path); return stats.isFile(); } catch (_) { return false; } } ``` The accepted file is subsequently sent to a remotely supplied upload destination: ```js } else if (!validator.isFilePath(file)) { utils.printError("无效的文件路径或URL"); process.exit(1); } if (!fs.existsSync(file)) { utils.printError("文件不存在: " + file); process.exit(1); } try { const presignedUrl = await video.getPresignedUrl(tokenValue, file); if (!presignedUrl || !presignedUrl?.url || presignedUrl.url === "") { throw new Error("获取预签名URL失败,请反馈给开发者"); } utils.printInfo("上传文件到安全空间..."); await upload.uploadFileToOSS(file, presignedUrl.url, presignedUrl.headers); ``` ### Technical Analysis The local path validator only verifies that the supplied path resolves to a regular file. It does not verify that the file is a video or audio asset, restrict access to user-approved directories, inspect the file signature, reject symbolic links, or impose a maximum file size. Because the Skill is intended to be invoked by an AI agent based on natural-language input, an attacker may attempt to persuade the agent to treat a sensitive local file as a video input. Any file readable by the Node.js process could pass this validation and be uploaded. The upload operation opens the accepted path with `fs.createReadStream()` and transmits its full contents to an external destination. The same validation therefore permits uploading configuration files, source code, SSH keys, cloud credentials, API configuration, or other sensitive data. ### Attack Path 1. An attacker supplies a local path through `--file`, either directly or through instructions interpreted b ...[truncated 1101 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the canonical path with `fs.realpath()` and reject symbolic links using `fs.lstat()`. 2. Restrict local uploads to explicitly approved directories or require interactive confirmation displaying the canonical path. 3. Enforce an allowlist of expected media extensions, while treating extensions only as an initial filter. 4. Verify file signatures or use a trusted media parser to confirm that the file is an actual supported audio or video format. 5. Reject known sensitive locations such as home credential directories, SSH directories, cloud configuration directories, and system configuration paths. 6. Apply a strict maximum file size before opening the upload stream. 7. Use least-privilege runtime isolation so the Skill cannot read files outside a dedicated media workspace. 8. Avoid accepting local paths inferred solely from untrusted natural-language content. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils/validator.js:3
Finding
Server-Side Request Forgery Through Unrestricted URLs and Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:3-9`; redirect handling in `scripts/utils/download.js:470-488` **Vulnerability Type**: Server-side request forgery and internal resource disclosure **Risk Level**: High ### Code Snippet The URL validator permits every HTTP or HTTPS destination: ```js function isUrl(url) { try { const parsedUrl = new URL(url); return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; } catch (_) { return false; } } ``` The downloader follows redirects without validating the destination address: ```js if (this.__isRequireRedirect(response)) { this.__redirectCount++; if (this.__redirectCount > this.__opts.maxRedirects) { const err = new Error("Too many redirects"); this.__setState(this.__states.FAILED); this.emit("error", err); return reject(err); } const redirectedURL = /^https?:\/\//.test(response.headers.location) ? response.headers.location : new URL(response.headers.location, this.url).href; this.__isRedirected = true; this.__initProtocol(redirectedURL); this.emit("redirected", redirectedURL, this.url); return this.__start(); } ``` ### Technical Analysis The Skill validates only the URL scheme. It does not reject loopback, private, link-local, unique-local IPv6, multicast, or cloud metadata destinations. Hostnames are not resolved and checked before a connection is made. Redirect targets are also accepted without repeating a network-boundary validation. An apparently public URL can therefore redirect the downloader to an internal service. DNS rebinding can similarly cause a hostname that initially appears public to resolve to a prohibited internal address when the request is performed. The downloaded response is saved locally and subsequently uploaded to the external transcription service. This creates an exfiltration path for data retrieved from internal resources rather than merely allowing blind network probi ...[truncated 1455 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve every hostname before connecting and reject all loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 ranges. 2. Explicitly block common metadata destinations, including link-local metadata addresses and their DNS aliases. 3. Repeat the complete validation process for every redirect target. 4. Prevent DNS rebinding by connecting only to the validated resolved address while preserving the intended TLS server name. 5. Reject URLs containing embedded credentials, unusual ports, or IP-address representations designed to bypass filters. 6. Prefer a strict allowlist of public video-provider domains when operationally possible. 7. Run downloads in a network sandbox that cannot reach localhost, private networks, cloud metadata services, or privileged infrastructure. 8. Do not automatically upload downloaded content until its source and media type have been validated. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/utils/helper.js:27
Finding
Unbounded Downloads Permit Disk and Process Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/helper.js:27-38`; downloader defaults in `scripts/utils/download.js:35-54` **Vulnerability Type**: Unrestricted resource consumption **Risk Level**: Medium ### Code Snippet The download configuration does not establish a size or total-duration limit: ```js async function download(url, path) { return new Promise((resolve, reject) => { const options = { retry: { maxRetries: constants.CREATE_MAX_ATTEMPTS, delay: constants.RETRY_INTERVAL, }, override: { skip: true, skipSmaller: true }, }; let progressLog = ""; const dl = new Downloader(url, path, options); ``` The downloader's default request timeout is disabled: ```js this.__defaultOpts = { body: null, retry: false, method: "GET", headers: {}, fileName: "", timeout: -1, metadata: null, override: false, forceResume: false, removeOnStop: true, removeOnFail: true, maxRedirects: 10, ``` The response is streamed directly to disk without a byte limit: ```js this.__fileStream = fs.createWriteStream(this.__filePath, {}); ``` ### Technical Analysis The downloader does not enforce a maximum accepted `Content-Length`, track downloaded bytes against a configured limit, or apply an overall operation deadline. Its default timeout is `-1`, and the caller does not override it. A malicious server can return an extremely large response, omit `Content-Length`, continuously stream data, or deliberately send data very slowly. Because the response is piped directly into a file, the process can consume all available disk space. A slow or endless response can also keep the Node.js process and associated resources occupied indefinitely. Retries can increase resource consumption when network failures are induced. The 24-hour cleanup mechanism does not mitigate an actively growing file or guarantee sufficient free space during the current execution. ### Attack Path 1. An attacker sup ...[truncated 940 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a maximum supported download size based on the service's legitimate media limits. 2. Reject responses whose declared `Content-Length` exceeds that limit. 3. Count streamed bytes independently of `Content-Length` and abort the request immediately when the limit is exceeded. 4. Configure separate connection, inactivity, and total-operation timeouts. 5. Abort transfers whose average rate remains below a safe threshold for an extended period. 6. Check available disk capacity before starting and reserve sufficient headroom for other services. 7. Delete partial files securely when a limit or timeout is reached. 8. Apply process-level and filesystem quotas to the download sandbox. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/api/video.js:11
Finding
Full Local Filesystem Paths Are Disclosed to the Remote API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/api/video.js:11-27` **Vulnerability Type**: Unnecessary local metadata disclosure **Risk Level**: Low ### Code Snippet ```js async function getPresignedUrl(token, filename) { if (!token || typeof token !== "string") { throw new Error("token 必须是非空字符串"); } if (!filename || typeof filename !== "string") { throw new Error("filename 必须是非空字符串"); } const data = { file: filename }; const response = await requestApi( "/api/video/presign", token, data, constants.CREATE_MAX_ATTEMPTS, "预上传", ); ``` ### Technical Analysis The `filename` argument is the path supplied to the command. For local files, this may be an absolute filesystem path containing usernames, home-directory names, customer identifiers, project names, mount points, or internal directory structure. The entire value is serialized into the request body sent to `www.guaikei.com`. A presigning service generally needs only a sanitized basename, generated object identifier, extension, or media type. Sending the complete local path exceeds the minimum data required for the operation. ### Attack Path 1. A user selects a local video with a sensitive or descriptive absolute path. 2. The path is passed unchanged to `getPresignedUrl()`. 3. The method places the complete value in the `file` JSON property. 4. `requestApi()` sends the JSON body to the remote API. 5. The remote service receives local host metadata that is not necessary to upload the file content. ### Impact Assessment The issue exposes metadata rather than file contents by itself. Disclosed paths may reveal operating-system usernames, organization or customer names, internal project structure, mounted storage conventions, or other environmental details. The remote service does not obtain additional local privileges through this behavior. The impact is limited to confidentiality and privacy, but the metadata may assist social engineering or ta ...[truncated 58 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the full path with `path.basename(filename)` before constructing the API request. 2. Sanitize the basename and remove control characters or path separators. 3. Prefer a randomly generated object identifier rather than a user-derived name. 4. Send only the validated media type and extension if that is all the presigning service requires. 5. Document all metadata transmitted to the external service. 6. Add tests confirming that absolute directory information never appears in outbound request bodies or logs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/utils/upload.js:10
Finding
Remote API Can Select an Unrestricted HTTPS Upload Destination<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/upload.js:10-42`; response use in `scripts/video2text/index.js:140-149` **Vulnerability Type**: Insufficient validation of a remote-controlled data destination **Risk Level**: Medium ### Code Snippet The caller trusts the URL and headers returned by the remote API: ```js const presignedUrl = await video.getPresignedUrl(tokenValue, file); if (!presignedUrl || !presignedUrl?.url || presignedUrl.url === "") { throw new Error("获取预签名URL失败,请反馈给开发者"); } utils.printInfo("上传文件到安全空间..."); await upload.uploadFileToOSS(file, presignedUrl.url, presignedUrl.headers); ``` The upload implementation validates only the scheme: ```js async function uploadFileToOSS(filename, presignedUrl, headers) { const url = new URL(presignedUrl); if (url.protocol !== "https:") { throw new Error("上传URL必须是HTTPS协议"); } return new Promise((resolve, reject) => { const fileStats = fs.statSync(filename); const totalSize = fileStats.size; let uploadedSize = 0; let settled = false; const fileStream = fs.createReadStream(filename); const uploadHeaders = Object.assign({}, headers); uploadHeaders["Content-Length"] = String(totalSize); if (!uploadHeaders["Content-Type"]) { uploadHeaders["Content-Type"] = "application/octet-stream"; } const options = { host: url.hostname, path: url.pathname + url.search, method: "PUT", headers: uploadHeaders, }; ``` ### Technical Analysis The presigning API controls the upload hostname, path, query string, and most request headers. The client verifies only that the URL uses HTTPS. It does not restrict the hostname to a documented object-storage provider, reject private or loopback destinations, or constrain which remote-supplied headers are forwarded. As a result, a compromised or malfunctioning API response can direct the client to upload the selected file to any HTTPS endpoint. This behavior also conflict ...[truncated 1218 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Maintain a strict allowlist of approved object-storage hostnames and expected domain suffixes. 2. Resolve the hostname and reject loopback, private, link-local, reserved, and metadata-service addresses. 3. Reject IP-literal upload destinations unless explicitly required and pinned. 4. Permit only a narrow allowlist of upload headers required by the storage provider. 5. Validate the URL path and query structure expected for the approved presigning mechanism. 6. Consider cryptographically signing the destination metadata at the application level and verifying that signature in the client. 7. Accurately document all storage domains that may receive uploaded files. 8. Require explicit confirmation before sending sensitive local content to a destination outside the primary service domain. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the implementation only validates the token and emits promotional or contact text instead of performing the declared transcription workflow, the skill is deceptive and may improperly collect secrets or mislead operators into granting sensitive credentials. In a security context, undisclosed credential gating plus nonfunctional behavior is dangerous because it can be used for phishing-like token harvesting or policy evasion.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the implementation only validates the token and emits promotional or contact text instead of performing the declared transcription workflow, the skill is deceptive and may improperly collect secrets or mislead operators into granting sensitive credentials. In a security context, undisclosed credential gating plus nonfunctional behavior is dangerous because it can be used for phishing-like token harvesting or policy evasion.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the implementation only validates the token and emits promotional or contact text instead of performing the declared transcription workflow, the skill is deceptive and may improperly collect secrets or mislead operators into granting sensitive credentials. In a security context, undisclosed credential gating plus nonfunctional behavior is dangerous because it can be used for phishing-like token harvesting or policy evasion.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the implementation only validates the token and emits promotional or contact text instead of performing the declared transcription workflow, the skill is deceptive and may improperly collect secrets or mislead operators into granting sensitive credentials. In a security context, undisclosed credential gating plus nonfunctional behavior is dangerous because it can be used for phishing-like token harvesting or policy evasion.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README explicitly promotes cloud-side processing of local files and public video links, but it does not give a prominent, upfront warning that uploaded videos leave the user's device and are sent to a remote third-party service for processing. Because videos may contain sensitive personal, corporate, or regulated data, insufficient disclosure can lead users to unknowingly transmit confidential content off-device.

Whitespace Padding

Medium
Category
Prompt Injection
Content
4. 同时传入文件路径与任务ID,优先执行 `--id`,忽略 `--file`
5. 无自定义 prompt 时,默认完整转录视频全部文字

| 用户自然语言指令                                         | 自动生成命令                                                                                                |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 视频提取 https://example.com/video.mp4 中的文字          | `node scripts/video2text/index.js --file "https://example.com/video.mp4"`                                   |
| 把本地 /path/to/your/video.mp4 改成小红书风格的文案      | `node scripts/video2text/index.js --file "/path/to/your/video.mp4" --prompt "改写成小红书风格的文案"`       |
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Lp3

Medium
Category
MCP Least Privilege
Confidence
80% confidence
Finding
The skill declares use of an environment variable for authentication but does not define an explicit tool scope such as permissions or allowed-tools. In agent ecosystems, missing scope declarations can cause over-broad execution privileges or make it unclear what sensitive resources the skill may access, weakening review and policy enforcement.

Vague Triggers

Medium
Confidence
85% confidence
Finding
The activation description is broad enough to trigger on common writing, summarization, and analysis requests that may not involve video transcription. Over-broad routing can cause unnecessary transmission of user files or content to an external service, creating avoidable privacy and data-minimization risks.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The 'when to use' section lists many broad content-production intents without strict trigger boundaries, increasing the chance the agent invokes this skill for unrelated tasks. Because the skill uploads user-provided media or fetches remote URLs, mistaken activation can expose data to third parties unnecessarily.

Whitespace Padding

Medium
Category
Prompt Injection
Content
## 6. 🧠 自然语言 → 命令 映射(AI 执行参考)

| 用户自然语言指令                                     | 生成的命令                                                                                                  |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 提取 https://example.com/video.mp4 里的文字          | `node scripts/video2text/index.js --file "https://example.com/video.mp4"`                                   |
| 总结这个视频的核心观点 https://example.com/video.mp4 | `node scripts/video2text/index.js --file "https://example.com/video.mp4" --prompt "总结这个视频的核心观点"` |
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This JavaScript file contains natural-language strings and comments in Chinese for download progress and error reporting, indicating the skill forces a specific language for user-visible output. The policy allows locale-specific behavior only when it is clearly justified or when the user is given a language/locale choice, neither of which is present here.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The code emits user-facing error strings entirely in Chinese, including operational guidance, with no indication that the skill is China-specific or that users can select another language. This can violate language/locale policy for general-purpose skills because it imposes a specific language without user opt-in.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The skill prints all user-facing warning and status messages in Chinese, including error and recovery instructions, with no indication that language is configurable or user-selected. This creates a locale policy issue because the skill imposes a specific language without opt-in or documented regional scope.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The comment makes a privacy and retention guarantee that the code does not enforce: this function only uploads a local video to a remote presigned URL and contains no deletion, lifecycle, or verification logic. In a skill handling user videos for transcription, this mismatch can mislead users and integrators into believing sensitive media is automatically deleted, creating privacy, compliance, and trust risks if the backend or storage retains the data longer than promised.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The banner text on L07 is hardcoded in Chinese, which imposes a specific language on users without any opt-in or indication that the skill is region-specific. The policy explicitly calls for flagging language or locale constraints when the skill does not offer a language choice or document a justified limitation.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The description forces a specific language/locale in the skill metadata, which can conflict with language-choice policy when no opt-in or region-specific justification is provided. The file does not state that the skill is intended only for Chinese-speaking users or offer alternative language support.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
This file presents all user-facing natural-language output, including errors, usage text, and help content, only in Chinese. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified.

Static analysis

No suspicious patterns detected.