Back to skill

Security audit

guaikei-video-wenzi-fetcher

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its video-to-text purpose, but it needs review because it can upload any readable local file or downloaded URL content to a cloud service while overstating its network and privacy limits.

Install only if you are comfortable sending media, prompts, task IDs, API tokens, and possibly local path names to the provider. Use it in a sandbox with filesystem access limited to intended media files and network access limited away from internal services; do not let it process sensitive local paths, private keys, credentials, localhost/internal URLs, or untrusted redirected URLs.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/utils/validator.js:12
Finding
Arbitrary Readable Local Files Can Be Uploaded to Remote Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:12-19`; `scripts/video2text/index.js:124-147` **Vulnerability Type**: Missing file-type and path-scope validation **Risk Level**: High ### Vulnerable Code ```js // scripts/utils/validator.js:12-19 function isFilePath(path) { try { const stats = fs.statSync(path); return stats.isFile(); } catch (_) { return false; } } ``` ```js // scripts/video2text/index.js:124-147 } 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 `--file` parameter is documented as accepting video files, but validation only verifies that the supplied path resolves to a regular file. There is no validation of: - File extension or detected MIME type - Audio/video file signatures - Allowed source directories - Symbolic-link resolution - User confirmation for sensitive paths - Maximum file size Consequently, any file readable by the Node.js process can enter the upload workflow, including credentials, private keys, configuration files, source code, and customer documents. Because `fs.statSync()` follows symbolic links, a symbolic link to a sensitive regular file would also pass this check. Uploading a user-selected local video is necessary for the declared functionality, but unrestricted access to every readable local file exceeds the minimum privilege required for video transcription. ### Attack Path 1. An attacker supplies content or instructions that cause an AI agent to invoke the Skill with a sensitive path, such a ...[truncated 1074 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict local input to explicitly approved directories or paths selected directly by the user. 2. Resolve the canonical path with `fs.realpathSync()` and verify that it remains under an allowed root. 3. Use `fs.lstatSync()` to reject symbolic links before opening a file. 4. Validate both a strict extension allowlist and the actual file signature for supported audio/video formats. 5. Reject device files, pipes, sockets, and other special files. 6. Enforce a maximum input size before requesting upload authorization. 7. Require explicit confirmation when an agent attempts to upload a local file, displaying the canonical path and remote destination. 8. Run the Skill in a sandbox with filesystem access limited to user-provided media files. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils/validator.js:3
Finding
Unrestricted URL Download Enables SSRF and Internal Data Exfiltration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:3-10`; `scripts/utils/download.js:480-493`; `scripts/utils/download.js:910-930`; `scripts/utils/download.js:977-995` **Vulnerability Type**: Server-Side Request Forgery through unrestricted URLs and redirects **Risk Level**: High ### Vulnerable Code ```js // scripts/utils/validator.js:3-10 function isUrl(url) { try { const parsedUrl = new URL(url); return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; } catch (_) { return false; } } ``` ```js // scripts/utils/download.js:480-493 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(); } ``` ```js // scripts/utils/download.js:910-930 __getReqOptions(method, url, headers = {}) { const urlParse = new URL(url); const options = { protocol: urlParse.protocol, host: urlParse.hostname, port: urlParse.port, path: urlParse.pathname + urlParse.search, method, }; if (urlParse.username || urlParse.password) { options.auth = `${urlParse.username}:${urlParse.password}`; } if (headers) { options["headers"] = headers; } return options; } ``` ```js // scripts/utils/download.js:977-995 __initProtocol(url) { const defaultOpts = this.__getReqOptions( this.__opts.method, url, this.__headers, ); this.requestURL = url; if (url.indexOf("https://") > -1) { this.__protocol = https; defaultOpts.agent = this.__defaultHttpsAgent ...[truncated 2231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the hostname before every request and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges. 2. Apply the same validation to every redirect destination. 3. Restrict destination ports to an approved set, normally TCP 443 and optionally TCP 80. 4. Prefer an allowlist of supported public media domains where practical. 5. Protect against DNS rebinding by connecting only to a previously validated resolved address while preserving TLS hostname verification. 6. Reject hostnames such as `localhost`, internal DNS suffixes, and numeric or encoded representations of private addresses. 7. Limit redirect count and prevent HTTPS-to-HTTP downgrade redirects. 8. Isolate the downloader in a sandbox without access to internal networks or cloud metadata services. 9. Validate that downloaded content is a supported media format before uploading it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/api/video.js:8
Finding
Full Local Filesystem Paths Are Disclosed to the Remote API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/api/video.js:8-31`; `scripts/utils/request.js:77-96` **Vulnerability Type**: Excessive transmission of local metadata **Risk Level**: Medium ### Vulnerable Code ```js // scripts/api/video.js:8-31 /** * 获取文件上传的预签名URL和授权Headers * @param {string} token API令牌 * @param {string} filename 文件完整路径 * @returns {Promise<{url: string, headers: object} | null>} 预签名上传信息,包含url和headers;失败返回null * @throws {Error} 网络错误或认证失败时抛出 */ 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, "预上传", ); ``` ```js // scripts/utils/request.js:77-96 const params = { _: Date.now(), skill_name: skillName() }; const fullPath = `${path}?${querystring.stringify(params)}`; const jsonData = JSON.stringify(data); const options = { host: constants.BASE_URL, path: fullPath, method: "POST", headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(jsonData), TOKEN: token, }, }; return await request(options, jsonData); ``` ### Technical Analysis Before uploading file contents, the Skill submits the entire `filename` value to `www.guaikei.com` in the body of `/api/video/presign`. For local input, this can be an absolute filesystem path such as: ```text /home/alice/customers/acme/confidential-board-meeting.mp4 ``` A full path is not necessary to create a presigned object-storage URL. A basename, generated object identifier, extension, and content type would be sufficient. Filesystem paths can reveal usernames, organization names, project names, mount points, directory structure, and sensitive document titles. This transmission exceeds the minimu ...[truncated 996 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the full path with `path.basename(filename)` before constructing the request. 2. Prefer a randomly generated object name and send only the required extension and detected content type. 3. Remove usernames, directory components, and other local metadata from all API payloads and logs. 4. Document every field transmitted to the remote service and its retention policy. 5. Add an automated test asserting that presign requests never contain path separators or absolute paths. 6. If the service requires a display name, sanitize it and obtain explicit user consent before transmitting it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/utils/helper.js:25
Finding
Unbounded Remote Downloads Permit Disk Exhaustion and Insecure Temporary-File Retention<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/helper.js:25-36`; `scripts/utils/download.js:464-475`; `scripts/utils/download.js:526-565`; `scripts/utils/utils.js:24-29`; `scripts/config/constants.js:8` **Vulnerability Type**: Unbounded resource consumption and unsafe temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```js // scripts/utils/helper.js:25-36 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); ``` ```js // scripts/utils/download.js:464-475 if (!this.__isResumed) { this.__total = parseInt(response.headers["content-length"]) || null; this.__resetStats(); } if (this.__isResumed && response.statusCode === 200) { this.__isResumed = false; this.__total = parseInt(response.headers["content-length"]) || null; this.__resetStats(); } ``` ```js // scripts/utils/download.js:526-565 const _fileName = this.__getFileNameFromHeaders(response.headers); this.__filePath = this.__getFilePath(_fileName); this.__fileName = this.__filePath.split(path.sep).pop(); if (fs.existsSync(this.__filePath)) { const downloadedSize = this.__getFilesizeInBytes(this.__filePath); const totalSize = this.__total ? this.__total : 0; if ( typeof this.__opts.override === "object" && this.__opts.override.skip && (this.__opts.override.skipSmaller || downloadedSize >= totalSize) ) { this.emit("skip", { totalSize: this.__total, fileName: this.__fileName, filePath: this.__filePath, downloadedSize: downloadedSize, }); this.__setState(this.__states.SKIPPED); return resolve(true); } } this.__fileStream = fs.createWriteStream(this.__filePath, {}); ... readable.on("data", (chunk) => this ...[truncated 2317 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a maximum accepted media size based on service requirements. 2. Reject responses whose `Content-Length` exceeds that limit before creating the destination file. 3. Maintain a streaming byte counter and abort the response and delete the partial file when the limit is exceeded. 4. Configure connection, response, idle, and total-operation timeouts. 5. Use `fs.mkdtemp()` under the operating system's temporary directory. 6. Create temporary directories with mode `0700` and files with mode `0600`. 7. Use unique, randomly generated filenames rather than predictable server-derived names. 8. Delete downloaded files immediately after upload in a `finally` block, including on upload or processing failure. 9. Preserve expiration-based cleanup only as a fallback for crash recovery. 10. Apply process-level filesystem quotas or sandbox limits as defense in depth. ]]>
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 (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The reported implementation appears centered on token/config handling and promotional messaging rather than the declared media-processing function. This is especially risky because the skill explicitly asks for a secret token and user-supplied files/URLs; when the core behavior is misdescribed, that combination can be used to harvest credentials, drive users to off-platform contact, or conceal non-obvious execution paths.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The reported implementation appears centered on token/config handling and promotional messaging rather than the declared media-processing function. This is especially risky because the skill explicitly asks for a secret token and user-supplied files/URLs; when the core behavior is misdescribed, that combination can be used to harvest credentials, drive users to off-platform contact, or conceal non-obvious execution paths.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The reported implementation appears centered on token/config handling and promotional messaging rather than the declared media-processing function. This is especially risky because the skill explicitly asks for a secret token and user-supplied files/URLs; when the core behavior is misdescribed, that combination can be used to harvest credentials, drive users to off-platform contact, or conceal non-obvious execution paths.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The reported implementation appears centered on token/config handling and promotional messaging rather than the declared media-processing function. This is especially risky because the skill explicitly asks for a secret token and user-supplied files/URLs; when the core behavior is misdescribed, that combination can be used to harvest credentials, drive users to off-platform contact, or conceal non-obvious execution paths.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The reported implementation appears centered on token/config handling and promotional messaging rather than the declared media-processing function. This is especially risky because the skill explicitly asks for a secret token and user-supplied files/URLs; when the core behavior is misdescribed, that combination can be used to harvest credentials, drive users to off-platform contact, or conceal non-obvious execution paths.

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
89% confidence
Finding
The skill requests access to an environment secret (`GUAIKEI_API_TOKEN`) but does not declare an explicit tool scope such as `permissions` or `allowed-tools`. In practice this weakens least-privilege controls and makes secret use less transparent to the host, increasing the risk of unintended secret exposure or overbroad execution privileges.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The invocation guidance tells agents to prioritize this skill for a very broad range of common content-analysis intents, which increases the chance of over-triggering. Overbroad activation is dangerous because it can cause unintended transmission of user-provided media, links, or local file paths to an external service when a narrower or local tool would have been more appropriate.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The natural-language-to-command mapping is permissive and lacks strong disambiguation rules, encouraging automatic command generation from ambiguous user requests. In context, that can lead to accidental reuse of `--id last`, unintended upload/processing of local paths or URLs, and external handling of sensitive media without sufficiently explicit user confirmation.

Whitespace Padding

Medium
Category
Prompt Injection
Content
当用户用自然语言下达指令时,按以下映射生成命令,保证识别与执行一致:

| 用户自然语言指令                                     | 生成的命令                                                                                                  |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 提取 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.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The package metadata advertises a very broad set of capabilities and activation contexts, including local file handling, external platform links, transcription, rewriting, translation, and content generation, without any stated trigger boundaries or user-consent constraints. In an agent ecosystem, overly broad scope increases the chance of unintended invocation on sensitive media or ambiguous user requests, which can lead to privacy exposure or misuse of fetched third-party content.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code sends the provided API token to a remote API via `requestApi`, which is a safety-relevant network operation involving credentials. While the functions have internal docstrings, those comments describe parameters and errors but do not warn users that authentication data will be transmitted over the network.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This JavaScript file uses Chinese-only natural-language strings for errors, comments, and generated help output, such as the module description, validation errors, and usage text. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified, which is not present here.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code downloads data from arbitrary URLs, writes it to disk, and can delete partial files on stop/failure via fs.unlink, but the file contains no confirmation prompt, print/log statement, or explanatory comment warning users about these side effects. Because this is a utility module rather than an explicitly named deploy/destructive skill, those safety-relevant behaviors are not clearly disclosed within the code itself.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code sends arbitrary request data plus a bearer-style TOKEN header to an external API endpoint, but the file contains no mechanism for user disclosure, consent, or minimization before transmitting potentially sensitive video-derived content. In the context of a video-to-text skill, uploaded material may contain personal data, confidential speech, or regulated content, so silent exfiltration to a cloud service materially increases privacy and compliance risk.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The file emits all user-facing status and warning messages exclusively in Chinese, including the key warning about token misconfiguration. This imposes a specific language on all users without offering any language choice or documenting a justified locale restriction, which matches the language/locale policy violation category.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The comment states a strong intent and data-handling guarantee: no leakage, no secondary use, and automatic deletion after processing. The implemented function only performs an HTTPS PUT upload to a presigned URL and has no code to delete the uploaded object or enforce post-processing cleanup, so the documentation overstates what the code guarantees.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The description, keywords, and author contact label are presented in Chinese, which can impose a specific language expectation on users without documenting language selection or locale scope. Under the language/locale policy, this can be a natural-language policy concern when the skill does not explicitly offer a user language choice or state that it is intended only for a Chinese-language audience.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The natural-language content in comments and thrown error messages is entirely in Chinese, which can impose a language/locale constraint on users and maintainers. The file does not indicate that the skill is region-specific or provide any opt-in or alternative language support.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
This code includes a Chinese-only doc comment and multiple Chinese status/error messages, which indicates the skill forces a specific language/locale in its natural-language output. The file does not show any user opt-in or configurable locale selection that would justify the restriction.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
Multiple user-visible error strings are hard-coded in Chinese, and the file provides no mechanism for language selection or opt-in. This can violate language/locale policy when skills are expected to respect user preferences or offer a choice.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The banner text is hard-coded in Chinese ("视频文案智能提取助手"), which indicates a fixed language choice in user-facing output. Under the policy rules, forcing a specific language without opt-in or documented justification is a natural-language policy concern.

Static analysis

No suspicious patterns detected.