Back to skill

Security audit

SkillPress — 技能锻造炉

Security checks for vulnerabilities and agentic risk

Overview

This skill is a skill generator, but its helper script can write or read SKILL.md files outside the intended skills folder if given a path-like skill name.

Install only if you are comfortable with a local skill-generation tool that writes persistent files. Until fixed, use simple slug names like lowercase letters, numbers, and hyphens only, and avoid running create or info with any value containing slashes, dots, .., or absolute paths. The publisher should add strict slug validation and resolved-path containment checks before broad use.

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/forge.py:45
Finding
Path Traversal Allows File Creation Outside the Skills Directory## Vulnerability Details **File Location**: `scripts/forge.py`, lines 45-85 **Vulnerability Type**: Unvalidated path traversal in the `create` command **Risk Level**: High ### Vulnerable Code ```python def create_skill(slug, display_name, description, emoji, steps, bins="python3"): """创建技能骨架""" skills_dir = Path(os.environ.get('OPENCLAW_WORKDIR', '.')) / 'skills' skill_dir = skills_dir / slug if skill_dir.exists(): print(f"⚠️ 技能已存在: {skill_dir}") cont = input("覆盖? (y/N): ") if cont.lower() != 'y': print("取消") return # 创建目录 (skill_dir / 'scripts').mkdir(parents=True, exist_ok=True) # 格式化步骤 steps_yaml = "" for i, step in enumerate(steps, 1): steps_yaml += f"{i}. **{step.strip()}** \n" # 格式化参数表 params_table = "| 参数 | 说明 |\n|------|------|\n| `<input>` | 输入内容 |\n| `<output>` | 输出目标 |\n" sample_usage = f"# 根据 {slug} 技能的具体功能填写使用示例" # 写入 SKILL.md content = SKILL_TEMPLATE.format( name=slug, display_name=display_name, description=description, emoji=emoji, bins=bins, sample_usage=sample_usage, steps_yaml=steps_yaml, params_table=params_table, ) (skill_dir / 'SKILL.md').write_text(content, encoding='utf-8') # 创建空脚本 scripts_dir = skill_dir / 'scripts' init_file = scripts_dir / '__init__.py' if not init_file.exists(): init_file.write_text('', encoding='utf-8') ``` ### Technical Analysis The `slug` value originates from `sys.argv[2]` and is appended directly to the configured skills directory without validation or a resolved-path containment check. Python's `pathlib` accepts both parent-directory components and absolute paths. Consequently, values such as `../../target` or `/tmp/target` cause `skill_dir` to refer to a location outside `${OPENCLAW_WORKDIR}/skills`. The f ...[truncated 1977 chars]
Remediation
## Remediation Suggestions - Restrict slugs to a conservative identifier format, such as `^[a-z0-9][a-z0-9-]*$`. - Reject absolute paths, path separators, `.` components, and `..` components. - Resolve both the skills root and candidate destination, then verify that the destination is strictly contained beneath the root: ```python import re if not re.fullmatch(r"[a-z0-9][a-z0-9-]*", slug): raise ValueError("Invalid skill slug") skills_dir = ( Path(os.environ.get("OPENCLAW_WORKDIR", ".")) / "skills" ).resolve() skill_dir = (skills_dir / slug).resolve() if skill_dir.parent != skills_dir: raise ValueError("Skill path escapes the skills directory") ``` - Consider symlink-based escapes. Validate resolved paths immediately before file operations and avoid following untrusted symlinks where platform APIs permit. - Refuse replacement by default and require an explicit `--force` option rather than relying on an interactive prompt. - Where feasible, create files with exclusive-create semantics to reduce accidental overwrites and race conditions.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/forge.py:94
Finding
Path Traversal Allows Disclosure of Out-of-Scope SKILL.md Files## Vulnerability Details **File Location**: `scripts/forge.py`, lines 94-107 **Vulnerability Type**: Unvalidated path traversal in the `info` command **Risk Level**: Medium ### Vulnerable Code ```python def show_info(slug): """显示技能信息""" skills_dir = Path(os.environ.get('OPENCLAW_WORKDIR', '.')) / 'skills' skill_dir = skills_dir / slug if not skill_dir.exists(): print(f"❌ 技能不存在: {slug}") print(f" 路径: {skill_dir}") return skill_file = skill_dir / 'SKILL.md' if not skill_file.exists(): print(f"⚠️ 技能目录存在但缺少 SKILL.md") return content = skill_file.read_text(encoding='utf-8') print(f"\n📋 技能信息: {slug}") print(f"{'='*50}") print(content[:500] + ("..." if len(content) > 500 else "")) ``` ### Technical Analysis The `info` command passes the user-controlled `slug` directly into `show_info()`. The function appends that value to the skills root without validating its syntax or verifying that the resolved path remains within the configured skills directory. A traversal value or absolute path can therefore select an arbitrary readable directory. If that directory contains a file named `SKILL.md`, the function reads it and prints up to its first 500 characters. The fixed filename limits the range of readable files, but it does not prevent disclosure of out-of-scope Skill definitions. ### Attack Path 1. The attacker identifies or guesses an external directory containing `SKILL.md`. 2. The attacker invokes the command with an absolute path or traversal sequence: ```bash python3 scripts/forge.py info ../../another-project ``` 3. The application constructs the external path without a containment check. 4. It opens the external directory's `SKILL.md`. 5. Up to the first 500 characters are emitted to standard output, where they may be exposed in terminal output, logs, or automation results. ### Impact Assessment ...[truncated 510 chars]
Remediation
## Remediation Suggestions - Apply the same strict slug allowlist used by the `create` command. - Resolve the configured skills root and requested Skill path before reading any file. - Reject the request unless the resolved Skill directory is a direct child of the configured root. - Validate the final resolved `SKILL.md` path to prevent symlink-based escapes: ```python skills_dir = ( Path(os.environ.get("OPENCLAW_WORKDIR", ".")) / "skills" ).resolve() skill_dir = (skills_dir / slug).resolve() skill_file = (skill_dir / "SKILL.md").resolve() if skill_dir.parent != skills_dir: raise ValueError("Skill path escapes the skills directory") if skill_file.parent != skill_dir: raise ValueError("SKILL.md resolves outside the skill directory") ``` - Return a generic validation error rather than printing out-of-scope resolved paths. - Add regression tests covering `..`, nested traversal, absolute paths, separators, and symlink targets.
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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 (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises behavior that creates files and scripts, and the static analysis indicates file read/write and environment-related capabilities, but the manifest does not declare any tool scope such as permissions or allowed-tools. This increases the chance of overbroad execution in hosts that rely on manifest-declared constraints, making unintended file access or modification easier if the skill is invoked in a permissive environment.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The manifest description and all user-facing invocation examples are written in Chinese, including the required trigger phrase at L19, with no indication that other languages are supported. This creates a language/locale policy concern because the skill appears to require a specific language without user opt-in or a documented regional justification.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This code embeds user-facing descriptions and command-line output in Chinese only, indicating the skill is designed to communicate in a specific language by default. The file does not provide an opt-in, fallback, or justification that this is a region-specific tool, which matches the language/locale policy violation criteria.

Static analysis

No suspicious patterns detected.