Back to skill

Security audit

Skill Audit by Raini

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local security scanner, but its recursive scan can follow symlinks outside the chosen skill directory and may expose local file snippets in terminal output.

Review this before installing if you plan to scan untrusted skill packages. It does not show evidence of malware or remote exfiltration, but run it only on directories you trust or in a sandbox because symlinks in scanned packages could cause it to read files outside the intended directory and print matching snippets to the console.

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

T09 · Insecure Skill Coding Practices

Warning
Location
src/audit.js:137
Finding

Symbolic-Link Traversal Allows Reads Outside the Scan Root

Content
View full analysis

Vulnerability Details

File Location: src/audit.js:137-158
Vulnerability Type: Symbolic-link traversal and unrestricted recursive file access
Risk Level: Medium

Vulnerable Code

js
function walk(currentPath) {
  if (!fs.existsSync(currentPath)) return;
  
  const stat = fs.statSync(currentPath);
  
  if (stat.isDirectory()) {
    // Skip node_modules and .git
    if (path.basename(currentPath) === 'node_modules') return;
    if (path.basename(currentPath) === '.git') return;
    
    const files = fs.readdirSync(currentPath);
    for (const file of files) {
      walk(path.join(currentPath, file));
    }
  } else if (stat.isFile()) {
    const ext = path.extname(currentPath).toLowerCase();
    if (extensions.includes(ext)) {
      const content = fs.readFileSync(currentPath, 'utf-8');
      const fileFindings = scanFile(currentPath, content);
      findings.push(...fileFindings);
    }
  }
}

Technical Analysis

The recursive scanner uses fs.statSync(), which follows symbolic links, and subsequently reads or traverses the resolved target. It does not use fs.lstatSync() to identify links, verify that each canonical path remains beneath the canonical scan root, or track previously visited directories.

Consequently, an untrusted Skill can contain a symbolic link whose target is outside the Skill directory. If the target is a directory, the scanner recursively traverses it. If it is a file with one of the supported extensions—.js, .ts, .mjs, .cjs, .json, or .md—the scanner reads its contents. A cyclic directory link can also cause unbounded recursion.

This behavior exceeds the minimum filesystem privileges required to scan files contained within the user-selected Skill directory.

Attack Path

  1. An attacker places a symbolic link in a Skill package.
  2. The link targets a sensitive file with a supported extension or a directory outside the package.
  3. A user runs skill-audit scan <skill-directory> or sca ...[truncated 1050 chars]
Remediation
View remediation

Remediation Suggestions

  1. Use fs.lstatSync() or fs.promises.lstat() before processing each entry and reject symbolic links by default.
  2. Canonicalize the scan root once with fs.realpathSync() and canonicalize every candidate before access.
  3. Verify that every candidate remains beneath the canonical root using a separator-aware containment check; do not rely on simple string-prefix comparison.
  4. Track visited directories by canonical path or filesystem identity to prevent cycles.
  5. Apply maximum traversal depth, file-count, and file-size limits to reduce denial-of-service exposure.
  6. Handle filesystem exceptions per entry so broken links, permission failures, and race conditions do not terminate the entire scan.
  7. Consider opening files with platform-supported no-follow protections where available to reduce time-of-check/time-of-use risks.
  8. Add regression tests covering links to external files, links to external directories, broken links, cyclic links, and similarly prefixed paths outside the root.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Confidence
85% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

�描所有已安装 skill skill-audit scan

扫描指定 skill

skill-audit scan moltdash

扫描本地目录

skill-audit scan ./my-skill

text

### `/skill-audit check <clawhub-slug>`
安装前检查 ClawHub 上的 skill。

```bash
skill-audit check some-skill

检测规则

🔴 高风险 (Critical)

  • 读取凭证文件: ~/.ssh/, ~/.env, credentials.json
  • 外发数据: fetch(), curl, webhook, POST 到未知 URL
  • 代码执行: eval(), exec(), child_process
  • 读取环境变量中的密钥: process.env.API_KEY

🟠 中风险 (Warning)

  • 网络请求到非知名域名
  • 文件系统遍历: fs.readdir(), glob
  • 动态 require/import
  • Base64 编码的字符串 (可能是混淆)

🟡 低风险 (Info)

  • 使用 shell 命令
  • 读写用户目录外的文件
  • 大量依赖包

输出示例

text
🔍 Skill Audit Report: suspicious-weather
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 35)May include surrounding context.

md
## 检测规则

### 🔴 高风险 (Critical)
- 读取凭证文件: `~/.ssh/`, `~/.env`, `credentials.json`
- 外发数据: `fetch()`, `curl`, `webhook`, `POST` 到未知 URL
- 代码执行: `eval()`, `exec()`, `child_process`
- 读取环境变量中的密钥: `process.env.API_KEY`

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · src/audit.js (reported line 19)May include surrounding context.

js
critical: [
    {
      name: 'credential_read',
      pattern: /(?:credentials|\.ssh|\.env|\.npmrc|\.aws|auth|secret|token).*(?:read|open|access|load)/gi,
      description: 'Reads credential files',
    },
    {

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The primary skill description is written in Chinese and the rest of the document continues in Chinese, with no indication that other languages are supported or that the user can choose their preferred language. This is a natural-language policy concern because it imposes a specific locale/language by default rather than offering opt-in or alternatives.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The --all mode recursively scans every installed skill under the user's workspace and reads matching files without any explicit confirmation, disclosure, or scope preview. While this is the tool's stated purpose, bulk inspection of local files can expose sensitive content from skills or embedded secrets to terminal output or downstream consumers if users are not clearly warned.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/audit.js:120

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/audit.js:268