Back to skill

Security audit

Local File

Security checks for vulnerabilities and agentic risk

Overview

This local file reader is purpose-aligned, but its weak path and size controls could let it read outside intended folders or overload the agent.

Review before installing. Use this only in a sandboxed workspace or after fixing path validation with canonical realpath containment, removing or explicitly configuring the hard-coded authorized directory, enforcing the documented file-size limit, and updating parser dependencies. Do not grant broad local-folder access unless you are comfortable with the agent reading supported files there.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
index.js:22
Finding
Allowed-Path Validation Can Be Bypassed to Read Unauthorized Files<![CDATA[ ## Vulnerability Details **File Location**: `index.js:22-48` **Vulnerability Type**: Improper path authorization and directory traversal **Risk Level**: High ### Vulnerable Code ```javascript module.exports = async function(context) { const filePath = context.args.path; const ext = path.extname(filePath).toLowerCase(); // 安全检查:限制可访问路径 const allowedRoots = [ process.env.OPENCLAW_WORKSPACE, 'D:\\个人' // 用户授权的路径 ]; if (!allowedRoots.some(root => filePath.startsWith(root))) { return { error: '路径不在允许范围内' }; } // 根据扩展名选择读取方式 switch (ext) { case '.txt': case '.md': case '.json': return readTextFile(filePath); case '.docx': return await readDocx(filePath); case '.pdf': return await readPdf(filePath); default: return { error: '不支持的文件格式' }; } }; ``` ### Technical Analysis The authorization check applies `String.prototype.startsWith()` directly to an attacker-controlled path. It does not normalize or canonicalize the target before comparing it with an allowed root. This permits several forms of authorization bypass: - **Directory traversal:** A path such as `/workspace/../secret.json` begins with `/workspace` as a string but resolves outside that directory. - **Prefix collision:** If `/workspace` is allowed, a path such as `/workspace-backup/secret.txt` also passes the prefix check. - **Symbolic-link traversal:** A symbolic link located under the workspace can refer to a file outside it. The submitted path passes the string comparison while the filesystem resolves it to an unauthorized target. - **Undefined workspace behavior:** If `OPENCLAW_WORKSPACE` is unset, JavaScript converts the `undefined` search value to the string `"undefined"` when evaluating `startsWith(undefined)`. The implementation therefore does not reliably fail closed. - **Overly broad hard-coded authorization:** The fixed `D:\个人` root grants access independently of the configured workspace and withou ...[truncated 1670 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `OPENCLAW_WORKSPACE` to be present and valid. Fail closed if it is absent. 2. Remove the hard-coded `D:\个人` root unless it is supplied through an explicit, trusted configuration mechanism. 3. Validate that the submitted path is a non-empty string. 4. Canonicalize the allowed root and target with `fs.realpathSync()` or their asynchronous equivalents. 5. Verify containment using `path.relative()` rather than string-prefix comparison. 6. Reject targets whose relative path is absolute, equals `..`, or begins with `..` followed by a path separator. 7. Perform containment validation after symbolic links have been resolved. 8. Reject non-regular files before reading them. 9. Account for Windows path separator and case-insensitivity behavior. Example containment approach: ```javascript const fs = require('fs'); const path = require('path'); function resolveAuthorizedFile(inputPath, configuredRoot) { if (typeof configuredRoot !== 'string' || configuredRoot.length === 0) { throw new Error('Workspace root is not configured'); } if (typeof inputPath !== 'string' || inputPath.length === 0) { throw new Error('Invalid file path'); } const root = fs.realpathSync(configuredRoot); const target = fs.realpathSync(path.resolve(root, inputPath)); const relative = path.relative(root, target); if ( relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) ) { throw new Error('Path is outside the allowed workspace'); } const stat = fs.statSync(target); if (!stat.isFile()) { throw new Error('Target is not a regular file'); } return target; } ``` If absolute input paths must be supported, resolve them independently and apply the same canonical containment test before any read or parser operation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:4
Finding
Missing File-Size Enforcement Enables Resource-Exhaustion Attacks<![CDATA[ ## Vulnerability Details **File Location**: `index.js:4-20` **Related Documentation**: `SKILL.md:14-15` **Vulnerability Type**: Uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code ```javascript // 读取文本文件 function readTextFile(filePath) { return fs.readFileSync(filePath, 'utf-8'); } // 读取 Word 文档(需要 mammoth 库) async function readDocx(filePath) { const mammoth = require('mammoth'); const result = await mammoth.extractRawText({ path: filePath }); return result.value; } // 读取 PDF(需要 pdf-parse 库) async function readPdf(filePath) { const pdf = require('pdf-parse'); const dataBuffer = fs.readFileSync(filePath); const data = await pdf(dataBuffer); return data.text; } ``` The documented restriction is: ```markdown ## 限制 - 只能读取工作区和用户明确授权的路径 - 大文件(>10MB)会拒绝 ``` ### Technical Analysis The documentation states that files larger than 10 MB will be rejected, but no file metadata or size validation is performed before reading or parsing. Text and PDF files are loaded completely into memory with `fs.readFileSync`. PDF processing then allocates additional memory during parsing. DOCX processing similarly parses a ZIP-based document through `mammoth`, which can consume substantially more memory than the compressed input size. The use of synchronous filesystem reads also blocks the Node.js event loop. A sufficiently large file, or a relatively small but parser-intensive document, can therefore degrade availability even when it does not terminate the process. No checks are present for: - Maximum compressed file size - Maximum extracted or parsed content size - Parser execution time - Maximum output length - Whether the target is a regular file - Concurrent parsing limits ### Attack Path 1. An attacker creates or identifies a large `.txt`, `.md`, `.json`, `.pdf`, or `.docx` file accessible to the skill. 2. The attacker invokes the skill with the file path. 3. The application performs no size check before processing th ...[truncated 1063 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve and authorize the target path before inspecting or reading it. 2. Call `fs.statSync()` or `fs.promises.stat()` and reject non-regular files. 3. Reject files larger than the documented 10 MiB limit before passing them to any parser. 4. Prefer asynchronous I/O to avoid blocking the event loop. 5. Impose a maximum extracted-text length. 6. Apply parser timeouts by running document parsing in a worker thread or isolated child process that can be terminated. 7. Limit concurrent document-parsing operations. 8. For compressed DOCX documents, enforce limits on both archive size and decompressed content. 9. Catch parser and memory-related errors and return a controlled failure response. Example initial size validation: ```javascript const MAX_FILE_SIZE = 10 * 1024 * 1024; async function validateFileSize(filePath) { const stat = await fs.promises.stat(filePath); if (!stat.isFile()) { throw new Error('Target is not a regular file'); } if (stat.size > MAX_FILE_SIZE) { throw new Error('File exceeds the 10 MiB size limit'); } } ``` This check should run after canonical path authorization and before `readFile`, `extractRawText`, or PDF parsing. Because compressed and parser-intensive documents may expand significantly, the input-size check should be combined with execution-time, output-size, memory, and concurrency controls. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Known Vulnerable Dependency: @xmldom/xmldom==0.8.11 — 15 advisory(ies): CVE-2026-83608 (xmldom: DocType `name` Injection Bypasses requireWellFormed); CVE-2026-41673 (xmldom: Uncontrolled recursion in XML serialization leads to DoS); CVE-2026-83605 (xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed) +12 more

High
Category
Supply Chain
Confidence
92% confidence
Finding
The lockfile pins @xmldom/xmldom 0.8.11 as a transitive dependency of mammoth, and the referenced advisories indicate parser/serializer weaknesses including input injection and denial-of-service conditions. In a local file manager skill that likely processes user-supplied DOCX/XML-derived content, this is materially relevant because crafted documents could trigger parser abuse or malformed XML handling.

Known Vulnerable Dependency: brace-expansion==1.1.12 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
82% confidence
Finding
brace-expansion 1.1.12 is present as a transitive development dependency and the cited issues are denial-of-service conditions from pathological input expansion. Because it is only marked dev:true here and not part of the skill's runtime document-processing path, the practical exploitability in deployed skill use is limited, though it still represents a supply-chain weakness in build or tooling contexts.

Known Vulnerable Dependency: flatted==3.3.4 — 2 advisory(ies): CVE-2026-32141 (flatted vulnerable to unbounded recursion DoS in parse() revive phase); CVE-2026-33228 (Prototype Pollution via parse() in NodeJS flatted)

High
Category
Supply Chain
Confidence
80% confidence
Finding
flatted 3.3.4 appears only as a transitive development dependency under flat-cache/eslint tooling, and the advisories describe parse-time DoS and prototype pollution risks. As locked, this is a real vulnerable package version, but in this file's context it is less dangerous because it is not part of the core local-file parsing runtime and would mainly affect developer or CI environments.

Known Vulnerable Dependency: js-yaml==4.1.1 — 4 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); CVE-2026-59869 (js-yaml: YAML merge-key chains can force quadratic CPU consumption); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
js-yaml 4.1.1 is included as a dev dependency through eslint configuration handling, and the advisories are primarily CPU-exhaustion issues during parsing of crafted YAML. This is a true vulnerable version, but the skill context makes it less dangerous because it does not appear to be used for runtime processing of untrusted content in the local file manager itself.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
All user-facing natural-language instructions and trigger descriptions are written exclusively in Chinese, which implies a fixed language/locale expectation. The file does not state that the skill is China-specific or provide any user opt-in or alternative language option.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The activation description lists generic phrases like “读取文件、查看文件、打开文件” without narrowing context or providing exclusions. These are common requests in everyday chat and could cause unintended invocation because the boundary between casual mention and deliberate skill use is unclear.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code includes natural-language comments and returned error strings exclusively in Chinese, such as the path and file-format error messages. The file does not offer any language selection or indicate that the skill is intentionally limited to Chinese-speaking users, which creates a locale-policy concern under the stated rules.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The package description is written only in Chinese, which indicates a language-specific presentation without any opt-in or alternative locale. Under the policy rules, forcing a specific language without user choice can be a natural-language policy violation unless the regional constraint is documented and justified.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"node": ">=18.0.0"
  },
  "dependencies": {
    "mammoth": "^1.6.0",
    "pdf-parse": "^1.1.1"
  },
  "devDependencies": {
Confidence
89% confidence
Finding
Using a caret range for the mammoth dependency allows future minor/patch releases to be installed automatically, which can introduce supply-chain risk, unexpected behavior changes, or newly shipped malicious code if the dependency ecosystem is compromised. In a file-processing skill that parses untrusted documents, dependency integrity matters more because parser libraries are part of the attack surface.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "mammoth": "^1.6.0",
    "pdf-parse": "^1.1.1"
  },
  "devDependencies": {
    "eslint": "^8.56.0"
Confidence
89% confidence
Finding
Using a caret range for pdf-parse permits automatic adoption of newer releases without explicit review, increasing supply-chain exposure and the chance of pulling in vulnerable or malicious code. This is more sensitive here because PDF parsers process attacker-controlled files and are a common source of security bugs.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"pdf-parse": "^1.1.1"
  },
  "devDependencies": {
    "eslint": "^8.56.0"
  },
  "openclaw": {
    "type": "skill",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.