Back to skill

Security audit

skill everyday

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it lets remote Clawhub data influence local filesystem paths and report content without enough containment.

Review before installing. Run it only in a workspace where listing neighboring skill directory names is acceptable, and prefer fixing slug validation/path containment plus Markdown/YAML escaping before routine use. Pin Playwright and use a lockfile if you install it.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/runner.mjs:219
Finding

Path Traversal Through an Unvalidated Remote Skill Slug

Content
View full analysis

Vulnerability Details

File Location: scripts/runner.mjs, lines 219-221, 255-259, and 284-286
Vulnerability Type: Path traversal and unintended filesystem access
Risk Level: High

Vulnerable Code

javascript
for (let i = 0; i < skills.length; i++) {
  const slug = skills[i].skill?.slug;
  if (!analyzedData.analyzed.includes(slug)) {
    targetSkill = skills[i];
    rank = i + 1;
    break;
  }
}
javascript
const skillsRoot = path.join(SKILL_DIR, '..');
const targetSkillDir = path.join(skillsRoot, skillInfo.slug);
let localFilesNote = '';
if (fs.existsSync(targetSkillDir)) {
  console.log(`📂 找到本地技能: ${targetSkillDir}`);
  localFilesNote = `\n### 本地技能目录\n\n\`${targetSkillDir}\`\n\n文件:\n\n\`\`\`\n${fs.readdirSync(targetSkillDir).join('\n')}\n\`\`\`\n`;
}
javascript
const reportPath = path.join(REPORTS_DIR, `${today}-${skillInfo.slug}.md`);
const latestPath = path.join(REPORTS_DIR, 'LATEST.md');
fs.writeFileSync(reportPath, report);
fs.writeFileSync(latestPath, report);

Technical Analysis

skillInfo.slug originates from the remotely retrieved Clawhub ranking response. The value is subsequently passed to path.join() without validating its character set or verifying that the resolved path remains inside the intended directory.

The directory-enumeration operation is directly exploitable because the slug is joined to skillsRoot without a safe prefix. Values such as .., ../.., or longer traversal sequences are normalized by Node.js and can point outside the skills directory. If the resulting path exists, fs.readdirSync() enumerates its entries and places their names in the generated report and standard output.

The report filename also incorporates the remote slug. Its date prefix makes straightforward traversal less reliable because an attacker may need a matching intermediate directory, but the construction still violates the expected path- ...[truncated 1836 chars]

Remediation
View remediation

Remediation Suggestions

  1. Apply strict allowlist validation before using a slug:

    javascript
    const SAFE_SLUG = /^[a-z0-9](?:[a-z0-9-]{0,126}[a-z0-9])?$/;
    
    if (typeof skillInfo.slug !== 'string' || !SAFE_SLUG.test(skillInfo.slug)) {
      throw new Error('Invalid skill slug');
    }
    
  2. Resolve the final path and verify containment:

    javascript
    function resolveContained(root, child) {
      const resolvedRoot = path.resolve(root);
      const resolvedPath = path.resolve(resolvedRoot, child);
      const relative = path.relative(resolvedRoot, resolvedPath);
    
      if (
        relative === '' ||
        relative.startsWith(`..${path.sep}`) ||
        relative === '..' ||
        path.isAbsolute(relative)
      ) {
        throw new Error('Path escapes the permitted root');
      }
    
      return resolvedPath;
    }
    
    const targetSkillDir = resolveContained(skillsRoot, skillInfo.slug);
    
  3. Generate report filenames from a validated or independently sanitized identifier rather than directly interpolating remote data.

  4. Reject slashes, backslashes, NUL characters, dot-only components, and platform-specific path separators.

  5. Where possible, compare against a trusted locally discovered set of skill directory names rather than converting arbitrary remote identifiers into paths.

  6. Handle filesystem errors without including sensitive absolute paths in externally visible reports.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/runner.mjs:143
Finding

Unsanitized Remote Content Persisted in Markdown and YAML Reports

Content
View full analysis

Vulnerability Details

File Location: scripts/runner.mjs, lines 143-145 and 246-286
Vulnerability Type: Stored Markdown injection, YAML front-matter injection, and indirect prompt-injection exposure
Risk Level: Medium

Vulnerable Code

javascript
const title = await page.title();
const description = await page.$eval('meta[name="description"]', (el) => el.content).catch(() => '');
const mainDesc = await page.$eval('main', (el) => el.innerText).catch(() => '');

return { title, description, content: mainDesc.slice(0, 5000) };
javascript
const introRaw =
  (skillInfo.summary && String(skillInfo.summary).trim()) ||
  (!skillDetail.error &&
    (skillDetail.description || (skillDetail.content && String(skillDetail.content).trim()))) ||
  '';
const introText =
  introRaw.length > 3500 ? `${introRaw.slice(0, 3500)}…` : introRaw || '';
javascript
let body;
if (knowledge.structured) {
  body = buildStructuredReport(skillInfo, knowledge, introText);
} else {
  body = `### 排名\n${rank}\n\n### 技能名称\n${skillInfo.name}\n\n### 技能标识\n\`${skillInfo.slug}\`\n\n### 分类\n${skillInfo.category}\n\n### 下载量 / 点赞数\n下载量:${skillInfo.downloads?.toLocaleString() || 'N/A'} | 点赞数:${skillInfo.stars?.toLocaleString() || 'N/A'}\n\n${buildDefaultReport(skillInfo, knowledge, introText)}`;
}

const report = `---
date: ${today}
rank: ${rank}
skill: ${skillInfo.slug}
---

${body}${localFilesNote}
---
*分析时间:${new Date().toLocaleString('zh-CN')} · 数据来源:Clawhub 榜单 + 技能详情页*
`;

const reportPath = path.join(REPORTS_DIR, `${today}-${skillInfo.slug}.md`);
const latestPath = path.join(REPORTS_DIR, 'LATEST.md');
fs.writeFileSync(reportPath, report);
fs.writeFileSync(latestPath, report);

Technical Analysis

Multiple values obtained from the remote ranking API and skill-detail page are interpolated directly into Markdown and YAML front matter. These include ...[truncated 2791 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat every value obtained from Clawhub as untrusted input.

  2. Serialize YAML front matter using a maintained YAML library instead of string interpolation:

    javascript
    import yaml from 'yaml';
    
    const frontMatter = yaml.stringify({
      date: today,
      rank,
      skill: skillInfo.slug,
    });
    
  3. Escape Markdown metacharacters according to the output context:

    javascript
    function escapeMarkdown(value) {
      return String(value)
        .replace(/\\/g, '\\\\')
        .replace(/([`*_[\]{}()#+.!|>-])/g, '\\$1');
    }
    
  4. Strip or neutralize raw HTML, image syntax, autolinks, and dangerous URL schemes. Permit only explicitly required schemes such as https:.

  5. Place remote descriptions in clearly marked quoted or fenced sections after safely handling fence delimiters.

  6. Add an explicit warning to generated reports stating that fetched content is untrusted and must not be interpreted as instructions.

  7. Do not provide generated reports directly to an AI Agent as trusted context. If Agent processing is required, isolate fetched text in a dedicated data field and instruct the consumer not to execute commands, follow embedded instructions, or invoke tools based solely on that text.

  8. Configure Markdown renderers to disable raw HTML and automatic remote-resource loading where possible.

  9. Add tests covering newlines, YAML delimiters, code fences, Markdown images, raw HTML, deceptive links, and instruction-like payloads.

Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (18)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
首次运行 `scripts/runner.mjs` 时会自动创建 `data/`、`data/reports/`(若不存在),并初始化 `data/analyzed.json`。

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
首次运行 `scripts/runner.mjs` 时会自动创建 `data/`、`data/reports/`(若不存在),并初始化 `data/analyzed.json`。

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
首次运行 `scripts/runner.mjs` 时会自动创建 `data/`、`data/reports/`(若不存在),并初始化 `data/analyzed.json`。

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
首次运行 `scripts/runner.mjs` 时会自动创建 `data/`、`data/reports/`(若不存在),并初始化 `data/analyzed.json`。

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
在**本技能根目录**(`SKILL.md` 所在目录)执行:

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
在**本技能根目录**(`SKILL.md` 所在目录)执行:

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill performs network access via Playwright to fetch data from external sites, but the manifest does not declare an explicit tool scope or allowed-tools boundary. Missing capability declarations weaken review and policy enforcement, making it easier for a skill with network behavior to operate with broader privileges than users or orchestrators expect.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill claims it only uses public Clawhub pages and local runtime state, but elsewhere documents reading local source code from neighboring skill directories. This mismatch is dangerous because it obscures the true data access scope, undermines informed consent, and may cause operators to approve a skill that can inspect local files they did not expect it to access.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documented workflow attempts to read local source directories of other skills by deriving sibling paths from the current skill directory. That expands the skill from public-web analysis into local filesystem inspection of unrelated content, which can expose proprietary code, secrets accidentally stored in skill folders, or other sensitive local data beyond the stated purpose.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
80% confidence
Finding

The documentation instructs use of npx playwright without pinning a specific version, which can result in fetching whatever package version is current at execution time. This creates a supply-chain risk: a compromised or breaking upstream release could execute unexpected code or alter browser automation behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger phrases are broad and can invoke the skill on generic requests like '分析一个 Clawhub 技能', without clearly constraining whether only public metadata should be used or whether local code inspection may occur. Overbroad activation increases the chance of accidental execution of network access and local file-reading behavior in contexts where the user did not intend that scope.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script inspects a sibling local skill directory derived from remotely sourced skillInfo.slug and embeds the directory path and filenames into the generated report. This exceeds the stated purpose of fetching and analyzing Clawhub hot skills, and can disclose local workspace structure or nearby repository contents to the report consumer without user awareness.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The report footer explicitly formats time using the 'zh-CN' locale, and the script's user-facing logs and generated report text are written in Chinese throughout. This imposes a specific language/locale on all users without offering a choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The README states that execution will create local state and overwrite a fixed file path (data/reports/LATEST.md) but does not present this as a clear safety warning before execution. In an agent setting, undisclosed filesystem writes increase the risk of surprising or unauthorized local state changes, even though the described writes are limited to a local data directory.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The dependency uses a caret range (^1.49.0), which allows newer minor versions to be installed without explicit review. This weakens build reproducibility and can unintentionally pull in vulnerable or behavior-changing releases from the supply chain.

Content

Scanner excerpt · package.json (reported line 6)May include surrounding context.

json
"private": true,
  "type": "module",
  "dependencies": {
    "playwright": "^1.49.0"
  }
}

Unverifiable Dependency: playwright has 1 known advisory(ies) (CVE-2025-59288 (Playwright downloads and installs browsers without verifying the authenticity of)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
72% confidence
Finding

The manifest includes Playwright without pinning an exact version, and Playwright has a known advisory related to downloading/installing browsers without verifying authenticity. Because the resolved version is not fixed here, consumers may install an affected release, creating supply-chain risk during browser download/setup.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/runner.mjs:169