Back to skill

Security audit

guaikei-video-transcript

Security checks for vulnerabilities and agentic risk

Overview

This is a real cloud transcription skill, but it can send more than just intended media to remote services because file, URL, and upload destinations are not tightly scoped.

Review this skill before installing. Use it only with media files and links you explicitly intend to send to GuaiKei's cloud service, avoid passing sensitive local paths, and avoid untrusted URLs unless it runs in a contained environment with restricted filesystem and network access. Treat the privacy and deletion claims as provider promises rather than guarantees enforced by this client.

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:11
Finding
Arbitrary Local Files Can Be Uploaded to the Remote Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:11-18`, with the upload sink at `scripts/video2text/index.js:120-136` **Vulnerability Type**: Missing file-type and path-scope validation **Risk Level**: High ### Vulnerable Code ```js function isFilePath(path) { try { const stats = fs.statSync(path); return stats.isFile(); } catch (_) { return false; } } ``` The accepted file is subsequently uploaded without validating that it is video or audio content: ```js 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-file validator only verifies that the supplied path refers to a regular file. It does not enforce: - A supported video or audio extension - A recognized media MIME type - Media magic-byte validation - A permitted workspace or media-directory boundary - Exclusion of sensitive operating-system, credential, or configuration paths The upload implementation uses `fs.createReadStream()` to transmit the complete accepted file. Consequently, any readable regular file available to the process can be treated as video input and sent to the remote storage service. This violates least privilege for a skill whose declared purpose is processing video and audio media. It is particularly relevant when command arguments are generated from untrusted natural-language instructions. ### Attack Path 1. An attacker influences an instruction processed by the agent and supplies a sensitive local path through `--file`. 2. Examples could include an environment file, cloud credential file, SSH private key, or another readable application configuratio ...[truncated 1063 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define an explicit allowlist of supported video and audio formats. 2. Validate both the filename extension and file magic bytes; do not rely exclusively on user-provided extensions or MIME types. 3. Resolve the supplied path with `fs.realpath()` and require it to remain inside an approved workspace or media directory. 4. Reject known sensitive locations, including home-directory credential stores, SSH directories, cloud configuration directories, environment files, and system configuration paths. 5. Use `lstat()` and reject symbolic links unless there is a specific requirement to support them. 6. Require explicit user confirmation before uploading files outside the current workspace. 7. Apply a maximum file-size limit before opening the upload stream. 8. Avoid sending the complete local path to the presign API; send only a sanitized basename when server-side naming requires it. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils/validator.js:3
Finding
Unrestricted URL Downloads Permit Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/validator.js:3-9`, with redirect handling at `scripts/utils/download.js:478-493` **Vulnerability Type**: Server-Side Request Forgery through unrestricted URLs and redirects **Risk Level**: High ### Vulnerable Code Initial URL validation only checks the protocol: ```js function isUrl(url) { try { const parsedUrl = new URL(url); return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; } catch (_) { return false; } } ``` Redirect destinations are followed without checking 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 intentionally accepts remote media URLs, but validation only limits the scheme to HTTP or HTTPS. It does not reject destinations resolving to: - IPv4 or IPv6 loopback addresses - Private network ranges - Link-local addresses - Cloud instance metadata services - Reserved, multicast, or unspecified addresses - Internal DNS names - Public hostnames that resolve or rebind to private addresses The downloader also follows up to ten redirects. Each redirect is converted into a new request without applying any network-range validation. A publicly accessible URL can therefore redirect the downloader to an internal endpoint. Content obtained from the target is written to a local temporary file and then uploaded to the external transcription service. This can ...[truncated 1539 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve every destination hostname before connecting. 2. Reject all loopback, private, link-local, unspecified, multicast, reserved, and documentation address ranges for IPv4 and IPv6. 3. Explicitly block common cloud metadata destinations, including link-local metadata addresses and provider-specific metadata hostnames. 4. Apply the same validation to the initial URL and every redirect target. 5. Pin the validated resolved address for the connection or revalidate immediately before connecting to mitigate DNS rebinding. 6. Reject URLs containing embedded credentials, unusual ports, malformed host representations, or IP-address encoding tricks. 7. Consider an allowlist of approved video-hosting domains if the intended use permits it. 8. Route outbound downloads through an egress proxy that denies private and metadata ranges. 9. Enforce a small redirect limit and prevent HTTPS-to-HTTP downgrade redirects where possible. 10. Add automated tests covering decimal, hexadecimal, IPv4-mapped IPv6, DNS rebinding, and redirect-based SSRF cases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/utils/helper.js:27
Finding
Unbounded Remote Downloads Can Exhaust Local Disk and Network Resources<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/helper.js:27-37`, with response processing at `scripts/utils/download.js:464-474` and file creation at `scripts/utils/download.js:526-549` **Vulnerability Type**: Unrestricted resource consumption **Risk Level**: Medium ### Vulnerable Code The download options do not define a size or 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 records the declared size but does not enforce a maximum: ```js 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(); } ``` It then creates a writable stream for the response: ```js 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); if (this.__isFileAlreadyDownloaded(downloadedSize)) { this.__setState(this.__states.FINISHED); this.emit("skip", { total: this.__total, name: this.__fileName, fileName: this.__fileName, filePath: this.__filePath, downloaded: downloadedSize, }); return resolve(true); } } this.__fileStream = fs.createWriteStream(this.__filePath, {}); ``` ### Technical Analysis There is no maximum accepted `Content-Length`, no streamed-byte limit, and no overall download deadline. A server may d ...[truncated 1648 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a strict maximum media size appropriate for the transcription service. 2. Reject responses whose declared `Content-Length` exceeds that maximum. 3. Count bytes received from the response stream and immediately abort the request when the limit is exceeded. 4. Enforce an overall wall-clock transfer deadline in addition to socket inactivity timeouts. 5. Limit retry attempts for partial or oversized responses and remove partial files after failure. 6. Check available disk capacity before starting a download. 7. Use per-task temporary directories with storage quotas where supported. 8. Delete downloaded temporary media immediately after successful upload or terminal failure instead of relying only on 24-hour cleanup. 9. Apply equivalent size checks before uploading user-supplied local files. ]]>

other

Warning
Location
scripts/utils/upload.js:10
Finding
Presigned Upload Destination Is Not Restricted to Documented Service Domains<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils/upload.js:10-45` **Vulnerability Type**: Unvalidated remote upload destination and privacy-boundary mismatch **Risk Level**: Medium ### Vulnerable Code ```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); // 复制预签名 headers,并补充 OSS PUT 必需字段 const uploadHeaders = Object.assign({}, headers); // 关键修复:明确 Content-Length,否则 Node 对流式 body 默认使用 chunked // Transfer-Encoding,阿里云 OSS 预签名 PUT 通常会拒绝而返回 400/403 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, }; const req = https.request( { ...options, timeout: constants.REQUEST_TIMEOUT }, (res) => { ``` The skill documentation states that network requests are limited to `https://www.guaikei.com`, but the upload implementation accepts any HTTPS hostname returned by the API. ### Technical Analysis The upload URL and request headers are supplied by the remote presign API. The client verifies only that the URL uses HTTPS. It does not independently validate: - The upload hostname - The hostname's resolved address - The destination port - Whether the domain belongs to an approved object-storage provider - Whether returned headers are within an expected allowlist Presigned object-storage uploads commonly require a different domain from the application's API domain, but that domain must be explicitly docume ...[truncated 1433 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Maintain an explicit allowlist of approved object-storage hostnames and exact domain suffixes. 2. Use exact hostname comparisons that prevent lookalike and suffix-confusion attacks. 3. Reject IP literals, localhost, private addresses, unexpected ports, and domains outside the approved storage infrastructure. 4. Resolve and validate the destination address before connecting. 5. Allow only the response headers required by the storage provider; reject unexpected authorization, forwarding, proxy, or host-related headers. 6. Document every required egress domain accurately instead of stating that only `www.guaikei.com` is contacted. 7. Bind presigned upload destinations to an expected storage bucket and region when those values are stable. 8. Add telemetry that records the destination hostname without logging sensitive query signatures. 9. Fail closed if the presign response does not conform to a strict schema. ]]>
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 (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill advertises media transcription but, per the finding, may also download arbitrary remote files, manage partial downloads, and write local files beyond the narrow user-visible task. If true in the implementation, this expands the attack surface to SSRF-like retrieval, unwanted persistence, and broader file-handling than users are led to expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill advertises media transcription but, per the finding, may also download arbitrary remote files, manage partial downloads, and write local files beyond the narrow user-visible task. If true in the implementation, this expands the attack surface to SSRF-like retrieval, unwanted persistence, and broader file-handling than users are led to expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill advertises media transcription but, per the finding, may also download arbitrary remote files, manage partial downloads, and write local files beyond the narrow user-visible task. If true in the implementation, this expands the attack surface to SSRF-like retrieval, unwanted persistence, and broader file-handling than users are led to expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill advertises media transcription but, per the finding, may also download arbitrary remote files, manage partial downloads, and write local files beyond the narrow user-visible task. If true in the implementation, this expands the attack surface to SSRF-like retrieval, unwanted persistence, and broader file-handling than users are led to expect.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The entire skill documentation, examples, prompts, and contact instructions are presented only in Chinese, and the workflow assumes Chinese-language usage without offering an alternative language or opt-in choice. This can violate language or locale policy where skills should not force a specific language absent user selection or a clearly documented regional justification.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The README defines broad natural-language trigger examples such as '总结一下这个视频的核心观点' and maps them directly to command execution patterns. In an agent setting, this can cause ordinary conversational requests to be misclassified as a tool invocation, leading to unintended upload of local files or fetching remote URLs for cloud transcription.

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
91% confidence
Finding
The skill declares access to an environment variable (`GUAIKEI_API_TOKEN`) but does not define an explicit tool scope such as `permissions` or `allowed-tools`. In agent environments, undeclared capability boundaries make it harder to enforce least privilege and can allow the skill to run with broader access assumptions than users expect.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The metadata and description contain dense keyword-style trigger language designed to maximize activation across many adjacent tasks. In context, this increases the chance of accidental invocation and unintended transfer of user media/content to the vendor service, which is risky because the skill is networked and depends on a third-party tokenized API.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The activation guidance is extremely broad and encourages the skill to trigger for a wide range of common writing, summarization, and content-creation intents. In an agentic system, overbroad routing can cause sensitive local files or user-provided links to be sent to a third-party cloud service when the user may only want generic text assistance.

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
87% confidence
Finding
The manifest description lists many generic use cases such as transcription, meeting notes, course breakdowns, livestream review, and content creation, but it does not specify how or when the skill should be invoked versus other skills. In a manifest file, this kind of broad natural-language scope can create ambiguous activation conditions and increase the chance of unintended invocation.

Natural-Language Policy Violations

Medium
Confidence
76% confidence
Finding
The description is entirely written in Chinese and advertises outputs such as Chinese-platform copywriting for Xiaohongshu and Douyin, but it does not indicate whether users can choose another language or locale. Under the policy, forcing a specific language or locale without opt-in can be a natural-language policy concern.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This file's comments, parameter error messages, and operation labels are all written in Chinese, including user-visible error text such as "token 必须是非空字符串" and labels passed to API requests. That enforces a specific language/locale without offering a user choice or documenting a justified locale constraint, which matches the policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This code contains natural-language content such as the comment at L24-L25 and multiple user-facing status/error messages in Chinese, but there is no indication that the skill is region-specific or that users can opt into this language. That can violate language/locale policy by forcing a specific language on users without choice.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The string literal instructs the user in Chinese and includes remediation guidance only in that language. This is a natural-language policy concern because the file imposes a specific language for user-facing messaging without any opt-in or alternative locale handling.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The string literals shown to users are entirely in Chinese, and the file provides no indication that this skill is China-specific or that users can choose another language. This creates a natural-language locale policy issue because the skill effectively forces a specific language for important operational messaging without opt-in.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The comment makes strong privacy and data-handling assurances ('no leakage, no retention, only used for transcription, automatically deleted') that are not enforced anywhere in this function. In a skill that uploads user videos/audio to a remote service, this creates a trust and compliance risk: users may submit sensitive recordings based on guarantees the code does not technically verify, enforce, or audit.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The banner text is hard-coded in Chinese, which imposes a specific language on users without any indication of opt-in, fallback, or regional justification. This matches the language/locale policy concern for natural-language content in code files.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
This file contains natural-language comments and user-facing CLI messages exclusively in Chinese, including errors and help output. Under the stated policy, forcing a specific language without offering user choice or documenting a justified locale constraint is a language/locale policy violation.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The natural-language documentation and user-facing strings in this file are written only in Chinese, indicating a fixed language choice with no opt-in or alternative locale. Under the stated policy, forcing a specific language without user choice is a locale-policy concern.

Static analysis

No suspicious patterns detected.