Back to skill

Security audit

OpenClaw Security Suite

Security checks for vulnerabilities and agentic risk

Overview

This security-review skill is purpose-aligned overall, but it can read any local file path and send the full contents to an LLM without containment or redaction.

Review before installing. Use this only on files and directories you intentionally want an LLM to see, and do not point the review action at secrets, credentials, private agent state, or broad filesystem paths. Treat scan results as advisory, not authoritative, because several advertised security layers are not enforced and the scanner coverage is incomplete.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • 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 (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
index.ts:31
Finding
Unrestricted Local File Disclosure Through AI Review<![CDATA[ ## Vulnerability Details **File Location**: `index.ts:31-42` **Vulnerability Type**: Arbitrary local file read and transmission to an LLM **Risk Level**: High ### Vulnerable Code ```ts } else if (action === "review") { if (!fs.existsSync(targetPath)) { throw new Error(`File not found: ${targetPath}`) } const code = fs.readFileSync(targetPath, "utf8") if (!ctx || !ctx.llm) { throw new Error("LLM context (ctx.llm) is required for semantic review.") } return aiReview(code, ctx.llm) ``` ### Technical Analysis The `path` input is controlled by the caller and is used directly with `fs.readFileSync`. The implementation does not: - Restrict the file to an approved project root. - Canonicalize the path before authorization. - Reject symbolic links. - Require a supported source-code extension. - Reject sensitive system or credential files. - Apply a maximum file-size limit. - Request confirmation before passing file contents to the LLM. Consequently, any file readable by the process can be selected for review. Its contents are then passed to `aiReview` and incorporated into an LLM request. Depending on the configured `ctx.llm`, this can move sensitive local data across a provider or trust boundary. ### Attack Path 1. An attacker obtains permission to invoke the Skill. 2. The attacker selects the `review` action. 3. The attacker supplies an absolute path to a process-readable sensitive file, or a symlink resolving to such a file. 4. `fs.existsSync` confirms that the target exists. 5. `fs.readFileSync` loads the complete file without authorization checks. 6. The contents are passed to `ctx.llm` through `aiReview`. 7. The sensitive data becomes available to the LLM context and potentially its remote provider, logging, or retention systems. ### Impact Assessment The vulnerability does not elevate operating-system privileges beyond those already held by the Skill process. It does, however, allow callers to cross i ...[truncated 421 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require an explicitly configured review root rather than accepting unrestricted absolute paths. 2. Resolve both the approved root and requested file with `fs.realpathSync`. 3. Verify that the resolved file remains inside the approved root using a path-aware containment check. 4. Use `fs.lstatSync` and reject symbolic links and non-regular files. 5. Allow only expected source-code extensions. 6. Explicitly reject known sensitive paths and files. 7. Impose a conservative file-size limit before reading or transmitting content. 8. Require explicit user confirmation before content is sent to an external LLM. 9. Document the configured LLM's data handling, logging, and retention behavior. 10. Perform authorization before existence checks where practical, to avoid leaking filesystem information. ]]>

T01 · Skill Instruction Hijacking

Error
Location
llm/ai_review.ts:3
Finding
Prompt Injection Can Manipulate AI Security Review Results<![CDATA[ ## Vulnerability Details **File Location**: `llm/ai_review.ts:3-20` **Vulnerability Type**: Untrusted code inserted into an instruction-bearing LLM prompt **Risk Level**: High ### Vulnerable Code ```ts export async function aiReview(code: string, llm: any) { const prompt = ` Analyze the following code for malicious behavior. Check for: - data exfiltration - shell execution - credential leak - system modification Code: ${code} Return JSON: risk_level reason ` const result = await llm.generate(prompt) return result } ``` ### Technical Analysis The reviewed source code is untrusted input, but it is interpolated directly into the same prompt that contains the review instructions. There is no system-message boundary, structured message separation, escaping mechanism, or explicit instruction telling the model to treat all instructions found inside the code as inert data. An attacker can place natural-language instructions in comments, strings, templates, or identifiers that direct the model to ignore the audit request, return a low-risk verdict, omit findings, or emit malformed output. The returned value is also not validated against a strict schema. This is particularly consequential because the LLM result is presented as a semantic security review. A malicious file can therefore influence the mechanism intended to assess that same file. ### Attack Path 1. An attacker creates a source file containing malicious behavior. 2. The attacker inserts a comment or string instructing the reviewer to ignore previous instructions and report the file as safe. 3. A user submits that file using the `review` action. 4. The complete file is concatenated into the reviewer prompt. 5. The LLM interprets the embedded text as an instruction rather than inert source data. 6. The model returns an attacker-influenced low-risk or malformed result. 7. A downstream user or system trusts the manipulated review and permits the malicious Skill. ### Impact Ass ...[truncated 546 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Place immutable audit instructions in a trusted system message. 2. Pass source code as a separate structured user-content field rather than concatenating it with instructions. 3. Explicitly state that comments, strings, and instructions contained in reviewed code are untrusted data and must never be followed. 4. Use strong, unambiguous delimiters and length limits as defense-in-depth, while recognizing that delimiters alone do not eliminate prompt injection. 5. Require structured output governed by a strict JSON schema. 6. Validate `risk_level`, `reason`, and all returned fields before use. 7. Treat malformed, incomplete, or schema-invalid output as `indeterminate`, never as safe. 8. Combine the LLM result with deterministic analysis rather than using it as an authoritative admission decision. 9. Test the reviewer against prompt-injection fixtures in comments, strings, encoded text, and nested source files. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scanner/skill_scanner.ts:9
Finding
Non-Recursive and Incomplete File Enumeration Produces False Safe Verdicts<![CDATA[ ## Vulnerability Details **File Location**: `scanner/skill_scanner.ts:9-18` **Vulnerability Type**: Incomplete security scan coverage **Risk Level**: High ### Vulnerable Code ```ts export function scanSkill(dir: string, policy: any, patterns: any) { const files = fs.readdirSync(dir) let results: any[] = [] for (const file of files) { if (!file.endsWith(".ts") && !file.endsWith(".js")) continue const full = path.join(dir, file) const code = fs.readFileSync(full, "utf8") ``` The final result treats an absence of detected issues as safe: ```ts return { safe: results.length === 0, results } ``` ### Technical Analysis The scanner only processes immediate directory entries ending in `.ts` or `.js`. It does not recursively inspect subdirectories and ignores other formats that can contain instructions or executable behavior, including: - `SKILL.md` - `.mjs` and `.cjs` - Shell scripts - Python scripts - Package lifecycle configuration - Nested JavaScript or TypeScript files This creates a fail-open security decision: unexamined content does not cause an incomplete or indeterminate result. Instead, the scanner returns `safe: true` whenever the limited set of inspected top-level files produces no findings. Directory entries are also read without explicit symbolic-link rejection. A symlinked `.js` or `.ts` entry can cause the scanner to read a file outside the requested directory. ### Attack Path 1. An attacker creates a Skill with a benign top-level `index.ts`. 2. The attacker places malicious JavaScript in a nested path such as `scripts/payload.js`, or uses an ignored executable format. 3. The Skill's top-level code loads or invokes that payload at runtime. 4. The scanner enumerates only immediate directory entries. 5. The nested or unsupported payload is never analyzed. 6. No issue is added to `results`. 7. The scanner returns `safe: true`, allowing users or automated systems to trust the incomplete v ...[truncated 454 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Recursively enumerate the complete Skill package. 2. Apply explicit traversal depth, file-count, and total-size limits to prevent denial of service. 3. Scan all relevant executable, instruction, manifest, and configuration formats. 4. Inspect `SKILL.md`, package lifecycle scripts, shell scripts, Python files, `.mjs`, `.cjs`, and nested JavaScript/TypeScript files. 5. Use `lstat` and canonical paths to reject symlinks that resolve outside the approved scan root. 6. Report unsupported, unreadable, oversized, or unparsed files as incomplete coverage. 7. Replace the binary `safe` decision with states such as `safe`, `unsafe`, and `indeterminate`. 8. Only return `safe` when every security-relevant file has been successfully enumerated and analyzed. 9. Add regression tests containing nested and alternate-format payloads. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scanner/ast_scanner.ts:17
Finding
AST and Keyword Rules Are Trivially Bypassable<![CDATA[ ## Vulnerability Details **File Location**: `scanner/ast_scanner.ts:17-48` **Vulnerability Type**: Incomplete dangerous-import and dangerous-call detection **Risk Level**: High ### Vulnerable Code ```ts traverse(ast, { ImportDeclaration(path: NodePath<ImportDeclaration>) { const moduleName = path.node.source.value if (policy.blocked_modules.includes(moduleName)) { issues.push({ type: "blocked_module", module: moduleName }) } }, CallExpression(path: NodePath<CallExpression>) { const callee = path.node.callee const name = callee.type === "Identifier" ? callee.name : undefined if (name && policy.blocked_functions.includes(name)) { issues.push({ type: "blocked_function", function: name }) } } }) ``` The relevant policy only performs exact-name matching: ```json { "blocked_modules": [ "child_process", "cluster" ], "blocked_functions": [ "exec", "spawn", "execSync" ] } ``` ### Technical Analysis Blocked modules are detected only when they appear in static `ImportDeclaration` nodes with an exact module string. This misses, among other variants: - `node:child_process` - CommonJS `require(...)` - Dynamic `import(...)` - Indirectly constructed module names Dangerous function calls are detected only when the callee is a direct `Identifier`. Member calls, aliases, computed properties, and simple data-flow transformations are not resolved. The keyword scanner provides only literal substring and regular-expression matching. Such checks are case- and formatting-sensitive and can be bypassed by whitespace, comments, string concatenation, computed properties, alternate import syntax, or aliases. ### Attack Path 1. An attacker imports a blocked module using a form not represented by the exact policy string, su ...[truncated 894 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Normalize built-in module names, including the `node:` namespace, before policy comparison. 2. Analyze static imports, CommonJS `require`, dynamic imports, and re-exports. 3. Resolve import bindings and track aliases to dangerous functions. 4. Inspect member expressions and computed-property calls. 5. Add limited data-flow analysis for destructuring, assignment, and alias propagation. 6. Detect dangerous APIs based on resolved provenance rather than identifier spelling alone. 7. Expand parsing support to applicable JavaScript syntax and explicitly report parse failures. 8. Treat keyword rules as supplementary signals rather than authoritative security controls. 9. Add bypass-oriented tests covering whitespace, comments, aliases, computed properties, dynamic imports, and `node:` module names. 10. Return an indeterminate result whenever complete semantic analysis cannot be performed. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
README.md:59
Finding
Advertised Runtime, Signature, and Permission Protections Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `README.md:59-65` **Vulnerability Type**: Security controls documented as active but unreachable from the entry point **Risk Level**: Medium ### Documented Security Controls ```md ## Security Layers | Layer | Method | What it catches | |-------|--------|-----------------| | **AST Scanner** | Babel AST traversal | Blocked module imports (`child_process`, `cluster`), dangerous function calls (`exec`, `spawn`) | | **Keyword Scanner** | Text matching | `eval(`, `__proto__`, `process.env`, `fs.writeFileSync`, etc. | | **VM Runner** | Node.js `vm` sandbox | Runtime behavior analysis with memory isolation and 1s timeout | | **Runtime Guard** | Argument inspection | Cloud metadata access (`169.254.169.254`), shell command execution | | **AI Review** | LLM analysis | Data exfiltration, credential leaks, system modification | | **Signature Verify** | SHA256 + public key | File integrity and authenticity | ``` The runtime guard exists but is not invoked by `index.ts`: ```ts export function runtimeGuard(args: any) { if (!args) return if (args.url && args.url.includes("169.254.169.254")) { throw new Error("Blocked cloud metadata access") } if (args.command) { throw new Error("Shell execution blocked") } } ``` The signature verifier and VM runner are similarly implemented as standalone helpers but are not imported or called by the entry point. The configured `allowed_permissions` and `blocked_permissions` fields are also not consumed. ### Technical Analysis Only `scanSkill` and `aiReview` are reachable through the exported `run` method. Repository-wide reference inspection found no integration of `runtimeGuard`, `runSandbox`, or `verifySignature` into the active scan or review path. As a result: - Metadata and shell arguments are not subject to the advertised runtime guard. - Scanned files are not authenticated before analysis or use. - No runtime behavior analysis occurs. ...[truncated 1227 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Integrate each claimed control into the exported execution path and add tests proving enforcement. 2. Remove documentation claims for controls that are experimental, optional, or unreachable. 3. Enforce permission policy through actual capability restrictions rather than unused configuration fields. 4. Define when signature verification is mandatory, establish trusted keys, and fail closed when verification fails. 5. Apply runtime protections at the actual network and process-execution boundaries, not merely to conventional argument names. 6. Do not use Node's `vm` module as a sandbox for hostile code. 7. If dynamic analysis is required, execute code in a separate disposable process or container with OS-level filesystem, network, process, resource, and privilege restrictions. 8. Add end-to-end tests showing that metadata access, shell execution, unsigned content, and prohibited permissions are blocked in real invocation paths. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description emphasizes security analysis capabilities: static scanning via AST/keywords and AI-powered semantic review for malicious behavior detection. The supplied code does not implement scanning, analysis, AST inspection, keyword matching, or AI review. Instead, it creates an isolated VM context and executes arbitrary code with a timeout. That is a materially different primary purpose and an undeclared capability. While a sandbox runner could be a supporting component of a larger security suite, this chunk by itself is code execution infrastructure, not security scanning behavior as described.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description says this skill is a comprehensive security analysis suite focused on static scanning and semantic review of code for malicious behavior. The supplied code does not implement scanning or behavior analysis. Instead, it performs a narrow cryptographic task: reading a file and verifying a provided signature against a public key. This is a materially different primary purpose from the declared one, so the description does not accurately represent the actual behavior of the code chunk.

External Script Fetching

High
Category
Supply Chain
Content
- **Dangerous Functions**: e.g., `exec()`, `spawn()`
- **Known Bad Keywords**: e.g., `eval(`, `__proto__`, `rm -rf`
- **Sensitive File Access**: e.g., `/etc/passwd`, `/.env`
- **Suspicious Regex Patterns**: e.g., `curl ... | bash`

### 2. AI Code Review (`action: "review"`)
Uses the active LLM context (`ctx.llm`) to semantically analyze a specific file for hidden threats:
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"\"nc ",
        "\"netcat ",
        "\"ssh ",
        "chmod 777"
    ],
    "sensitive_files": [
        "/etc/passwd",
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
"chmod 777"
    ],
    "sensitive_files": [
        "/etc/passwd",
        "/etc/shadow",
        "/.ssh",
        "/.env"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"chmod 777"
    ],
    "sensitive_files": [
        "/etc/passwd",
        "/etc/shadow",
        "/.ssh",
        "/.env"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
],
    "sensitive_files": [
        "/etc/passwd",
        "/etc/shadow",
        "/.ssh",
        "/.env"
    ],
Confidence
95% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"/etc/passwd",
        "/etc/shadow",
        "/.ssh",
        "/.env"
    ],
    "suspicious_urls": [
        "metadata.google.internal",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
"/.env"
    ],
    "suspicious_urls": [
        "metadata.google.internal",
        "169.254.169.254"
    ],
    "suspicious_patterns": [
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
],
    "suspicious_urls": [
        "metadata.google.internal",
        "169.254.169.254"
    ],
    "suspicious_patterns": [
        "curl .+\\|.+bash",
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Content
],
    "suspicious_urls": [
        "metadata.google.internal",
        "169.254.169.254"
    ],
    "suspicious_patterns": [
        "curl .+\\|.+bash",
Confidence
90% confidence
Finding
Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
"\"nc ",
        "\"netcat ",
        "\"ssh ",
        "chmod 777"
    ],
    "sensitive_files": [
        "/etc/passwd",
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The review action reads whatever file path is supplied, loads the full file contents into memory, and forwards that content to `aiReview(code, ctx.llm)`. If an attacker or untrusted caller can influence `targetPath`, this can exfiltrate sensitive local files, source code, secrets, or proprietary data to an external LLM service without any path restrictions, consent checks, or redaction.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"publish:skill": "./scripts/publish.sh"
    },
    "dependencies": {
        "@babel/parser": "^7.26.0",
        "@babel/traverse": "^7.26.0"
    },
    "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
    "dependencies": {
        "@babel/parser": "^7.26.0",
        "@babel/traverse": "^7.26.0"
    },
    "devDependencies": {
        "@types/babel__traverse": "^7.28.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: @babel/traverse has 1 known advisory(ies) (CVE-2023-45133 (Babel vulnerable to arbitrary code execution when compiling specifically crafted)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
82% confidence
Finding
The manifest references @babel/traverse with a non-exact version while that package has a known advisory history, making it impossible from this file alone to verify whether installs resolve to a safe release. In a security-scanning skill that parses and semantically reviews potentially adversarial code, using a parser/traversal library with uncertain patch status increases supply-chain and code-processing risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@babel/traverse": "^7.26.0"
    },
    "devDependencies": {
        "@types/babel__traverse": "^7.28.0",
        "@types/node": "^22.0.0",
        "typescript": "^5.6.0"
    }
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
    "devDependencies": {
        "@types/babel__traverse": "^7.28.0",
        "@types/node": "^22.0.0",
        "typescript": "^5.6.0"
    }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
        "@types/babel__traverse": "^7.28.0",
        "@types/node": "^22.0.0",
        "typescript": "^5.6.0"
    }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.