Back to skill

Security audit

Skill Auto Evolver

Security checks for vulnerabilities and agentic risk

Overview

This is a local skill health and reporting tool, but it needs review because malformed inputs can make it inspect unintended local folders and generate unsafe HTML reports.

Install only if you are comfortable with a local tool that reads skill source files and keeps a persistent usage database. Use it on trusted skill names and directories, avoid putting secrets or personal data into context, errors, comments, or user IDs, and avoid opening exported HTML reports from untrusted inputs until the HTML escaping and path validation issues are fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
skill_evolver/analyzer.py:74
Finding
Unvalidated Skill Name Allows Directory-Boundary Bypass<![CDATA[ ## Vulnerability Details **File Location**: `skill_evolver/analyzer.py:74`, with recursive file access at `skill_evolver/analyzer.py:258` **Vulnerability Type**: Path traversal and unauthorized filesystem analysis **Risk Level**: High ### Vulnerable Code ```python def analyze_skill(self, skill_name: str) -> SkillAnalysisResult: """ Analyze a single skill. """ skill_path = self.skills_dir / skill_name result = SkillAnalysisResult( skill_name=skill_name, skill_path=str(skill_path) ) if not skill_path.exists(): result.issues.append(AnalysisIssue( severity="critical", category="structure", message=f"Skill directory does not exist: {skill_path}" )) return result result.exists = True self._analyze_skill_md(result, skill_path) self._analyze_package_json(result, skill_path) self._analyze_python_code(result, skill_path) self._analyze_structure(result, skill_path) ``` The resulting path is subsequently traversed recursively: ```python def _analyze_python_code(self, result: SkillAnalysisResult, skill_path: Path): """Analyze Python source files.""" python_files = list(skill_path.rglob("*.py")) if not python_files: result.issues.append(AnalysisIssue( severity="info", category="structure", message="No Python source files found" )) return for py_file in python_files: try: content = py_file.read_text(encoding="utf-8") try: ast.parse(content) except SyntaxError as e: result.issues.append(AnalysisIssue( severity="critical", category="syntax", message=f"Python syntax error in {py_file.name}: {e}", line=e.lineno )) continue ``` ### Technical Analysis `skill_name` is joined directl ...[truncated 2059 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute skill names and names containing path separators or parent-directory components. 2. Resolve both the configured root and candidate path before accessing the filesystem. 3. Require the resolved candidate to remain beneath the resolved root: ```python def _resolve_skill_path(self, skill_name: str) -> Path: if not skill_name or Path(skill_name).is_absolute(): raise ValueError("Invalid skill name") root = self.skills_dir.resolve() candidate = (root / skill_name).resolve() try: candidate.relative_to(root) except ValueError as exc: raise ValueError("Skill path escapes the configured skills directory") from exc return candidate ``` 4. Consider enforcing a strict skill-name pattern such as `^[A-Za-z0-9][A-Za-z0-9._-]*$`. 5. Define and enforce a symlink policy. If symlinks are not required, reject skill directories and recursively encountered files that resolve outside the root. 6. Limit the number and total size of files analyzed to reduce denial-of-service exposure. 7. Add tests for absolute paths, `../` traversal, nested traversal, symlink escapes, and valid names. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
skill_evolver/reporter.py:308
Finding
Unescaped Report Values Allow HTML Active-Content Injection<![CDATA[ ## Vulnerability Details **File Location**: `skill_evolver/reporter.py:308-387` **Vulnerability Type**: Stored HTML and script injection **Risk Level**: High ### Vulnerable Code ```python def _report_to_html(self, report: Dict[str, Any]) -> str: """Convert a report to HTML.""" if "error" in report: return f"<h1>Error</h1><p>Error analyzing {report['skill_name']}: {report['error']}</p>" status_color = { "healthy": "#28a745", "warning": "#ffc107", "critical": "#dc3545" } html = f"""<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>{report['skill_name']} Health Report</title> <style> body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }} h1 {{ color: #333; }} .score {{ font-size: 48px; font-weight: bold; color: {status_color.get(report['status'], '#666')}; }} .status {{ display: inline-block; padding: 5px 15px; border-radius: 20px; color: white; background: {status_color.get(report['status'], '#666')}; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th, td {{ padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }} th {{ background: #f5f5f5; }} .section {{ margin: 30px 0; }} </style> </head> <body> <h1>{report['skill_name']} Health Report</h1> <p>Generated at: {report['generated_at']}</p> <div class="section"> <div class="score">{report['overall_score']}/100</div> <span class="status">{report['status'].upper()}</span> </div> """ for rec in report['recommendations']: html += f" <li>{rec}</li>\n" html += """ </ol> </div> </body> </html>""" return html ``` ### Technical Analysis The HTML renderer directly interpolates report fields into element content, the document title, CSS values, and list items without context-appro ...[truncated 1579 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every dynamic value according to its HTML context. For text and attribute values, use `html.escape(value, quote=True)`. 2. Prefer an auto-escaping template engine rather than constructing HTML through string interpolation. 3. Do not place untrusted data into CSS or other specialized contexts without strict allow-list validation. 4. Validate skill names independently, but do not rely on input validation as a replacement for output encoding. 5. Escape error messages and recommendations because they can include data derived from filesystem or caller-controlled input. 6. Consider adding a restrictive Content Security Policy when reports are served through HTTP: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:"> ``` 7. Add regression tests containing element-closing sequences, quotes, ampersands, event-handler attributes, and script elements. Tests should assert that the exported file contains encoded text rather than executable markup. A minimal encoding approach is: ```python from html import escape safe_skill_name = escape(str(report["skill_name"]), quote=True) safe_generated_at = escape(str(report["generated_at"]), quote=True) for recommendation in report["recommendations"]: html += f"<li>{escape(str(recommendation), quote=True)}</li>\n" ``` ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unpinned Dependency Installation Creates Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-13` **Additional Locations**: `README.md:23`, `SKILL.md:30` **Vulnerability Type**: Unbounded third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code The documented installation workflow executes: ```bash pip install -r requirements.txt ``` The complete dependency declaration is: ```text # Skill Evolver Dependencies # YAML parsing PyYAML>=6.0 # Git operations (optional) GitPython>=3.1.0 # Testing pytest>=7.0.0 # Code linting flake8>=6.0.0 ``` ### Technical Analysis Every dependency uses an open-ended lower bound. As a result, installations performed at different times can resolve to different package and transitive-dependency versions. A future release satisfying the declared constraint is accepted automatically without a repository review or integrity hash. Python package installation may execute build backend or installation-related code. Therefore, compromise of a direct or transitive dependency can cause attacker-controlled code to run during installation with the privileges of the user executing `pip`. The requirements file also installs testing and linting tools during the documented normal setup process. These packages are not required for the core runtime behavior and unnecessarily enlarge the dependency graph and supply-chain attack surface. No evidence was found that the currently named packages are malicious. This finding concerns the unsafe dependency-resolution policy and the resulting exposure to a future compromised compatible release. ### Attack Path 1. A direct or transitive dependency publishes a compromised release that still satisfies the broad minimum-version constraint. 2. A user follows the documented `pip install -r requirements.txt` installation procedure. 3. Pip resolves the compromised release because no exact version or integrity hash prevents selection. 4. Pip downloads the package and invokes its package build or installa ...[truncated 669 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate an audited lock file containing exact versions for direct and transitive dependencies. 2. Include package hashes and install with hash verification, such as: ```bash python -m pip install --require-hashes -r requirements.lock ``` 3. Separate runtime dependencies from development dependencies. Move `pytest` and `flake8` to a development-only requirements file. 4. Remove `GitPython` from runtime installation if the application does not use it. 5. Install dependencies inside a dedicated virtual environment under an unprivileged account. 6. Use dependency vulnerability scanning and automated update review rather than accepting arbitrary future versions. 7. Review lock-file changes before release and regenerate hashes only from trusted package indexes. 8. Consider disabling source builds where operationally practical and accepting only verified binary distributions for production installation. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
描述强调的是“监控使用情况、评估健康度、生成改进建议”的综合优化工具。实际代码确实实现了“生成改进建议”,也部分符合“评估健康度”这一宽泛说法,因为它会基于静态检查给出分数和问题列表。但代码没有任何使用情况采集、运行期监控、日志统计或行为分析逻辑,因此“监控使用情况”与实际行为明显不符。相反,代码的核心是静态分析技能文件与代码质量,包括解析 SKILL.md/package.json、AST 语法检查、风格规则检查、目录结构检查以及技能间评分对比。这说明描述与实际行为存在实质性偏差,属于部分但明显的能力不匹配。

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises and demonstrates file-reading and file-writing behavior through analysis, report export, and local database storage, but it does not declare any tool scope such as permissions or allowed-tools. This creates a trust and review gap: operators cannot easily tell that the skill may access local files and persist data, increasing the chance of unintended filesystem access or over-broad execution in permissive environments.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The manifest description and the user-facing documentation are written in Chinese throughout, with no indication that other languages are supported or that the locale restriction is intentional and justified. This can violate language/locale policy when a skill effectively forces one language without user opt-in.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill documents usage monitoring and feedback collection, and later specifies local SQLite storage, but it does not provide a clear privacy warning, retention notice, or guidance on what data may be captured in logs/comments. This can lead to inadvertent collection of sensitive operational data or personal information, especially because examples include free-form user comments and contextual logging.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The Database initializer creates a persistent directory under the user's home folder and initializes a SQLite database for logging skill usage and storing feedback. While the module docstring mentions storage in Chinese, there is no confirmation prompt or clear user-facing disclosure at the points where this persistence is set up, despite handling user data such as feedback, context, and user_id.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The manifest description is written only in Chinese ("自动分析和优化 OpenClaw Agent Skills 的工具"), which indicates a language-specific presentation without offering a user choice or documenting that this skill is intentionally region-specific. The policy requires avoiding forced language or locale constraints unless the user opts in or the limitation is clearly justified.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This Python file contains user-facing natural-language descriptions entirely in Chinese, including the module docstring describing the skill's purpose. Under the language/locale policy, forcing a specific language without user opt-in is a violation when no alternative or opt-in mechanism is documented.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The monitor persists arbitrary context data and raw error messages directly to the database, which can easily include secrets, personal data, prompts, tokens, or internal system details. In a monitoring component, this is particularly risky because callers may pass rich runtime context by default, creating a central store of sensitive data without redaction, minimization, or disclosure controls.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This Python file contains natural-language docstrings, error messages, and generated Markdown/HTML report text entirely in Chinese. The policy scope allows flagging language/locale violations in code string literals when a specific language is imposed without user opt-in or documented justification.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This code file contains natural-language strings and comments exclusively in Chinese, and the assertions require Chinese output such as "数据库已初始化", "已记录技能使用", and "健康度报告". That indicates a language constraint baked into the skill behavior without any visible opt-in or documented justification, which matches the locale-policy violation criteria.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
Module and class/function docstrings are written in Chinese, which imposes a specific language on operators or maintainers without any opt-in or justification. Under the language/locale policy, natural-language instructions should not force a specific language unless the constraint is documented and justified or alternatives are provided.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"python": ">=3.8"
  },
  "dependencies": {
    "pyyaml": "^6.0",
    "gitpython": "^3.1.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": {
    "pyyaml": "^6.0",
    "gitpython": "^3.1.0"
  },
  "devDependencies": {
    "pytest": "^7.0.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"gitpython": "^3.1.0"
  },
  "devDependencies": {
    "pytest": "^7.0.0",
    "flake8": "^6.0.0"
  },
  "bin": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "pytest": "^7.0.0",
    "flake8": "^6.0.0"
  },
  "bin": {
    "skill-evolver": "./skill_evolver/cli.py"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Skill Evolver Dependencies

# YAML parsing
PyYAML>=6.0

# Git operations (optional)
GitPython>=3.1.0
Confidence
95% confidence
Finding
PyYAML is specified with a lower-bound only constraint, so builds may resolve to different versions over time. This weakens supply-chain reproducibility and can allow deployment of a vulnerable or incompatible release if upstream changes or advisories emerge.

Unverifiable Dependency: PyYAML has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
PyYAML has known advisories, and because the manifest does not pin a specific version, there is no way to verify whether the installed release is affected. In the context of an auto-analysis/optimization skill that may process YAML-based configuration or skill files, this increases the risk that a vulnerable parser version could be introduced.

Unpinned Dependencies

Low
Category
Supply Chain
Content
PyYAML>=6.0

# Git operations (optional)
GitPython>=3.1.0

# Testing
pytest>=7.0.0
Confidence
95% confidence
Finding
GitPython is unpinned and may resolve to different versions across environments or over time. Because Git-related libraries can interact with local executables and repositories, lack of version pinning increases supply-chain and reproducibility risk.

Unverifiable Dependency: GitPython has 16 known advisory(ies) (CVE-2026-78676 (GitPython: Dormant multi-line git-config values are corrupted into live injected); CVE-2026-67325 (GitPython: Command Injection via git long-option prefix abbreviation bypass of C); CVE-2024-22190 (Untrusted search path under some conditions on Windows allows arbitrary code exe) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
GitPython has multiple known advisories, but the absence of version pinning means the actual installed version cannot be assessed. Since this skill may monitor or evolve agent skills and could plausibly inspect repositories, a vulnerable GitPython version could expose command-execution or repository-handling risks more directly than a purely passive library.

Unpinned Dependencies

Low
Category
Supply Chain
Content
GitPython>=3.1.0

# Testing
pytest>=7.0.0

# Code linting
flake8>=6.0.0
Confidence
92% confidence
Finding
pytest is declared with `>=` only, which permits uncontrolled version drift. Even though it is typically a test dependency, unpinned tooling can still introduce vulnerable packages or unstable builds in CI and development environments.

Unverifiable Dependency: pytest has 2 known advisory(ies) (CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
85% confidence
Finding
pytest has known advisories, and without a pinned version the environment may install an affected release. Because pytest is listed as a testing dependency rather than runtime functionality, the direct production impact is lower, but CI or developer environments could still be exposed.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pytest>=7.0.0

# Code linting
flake8>=6.0.0
Confidence
92% confidence
Finding
flake8 is unpinned, allowing future installations to pull different versions with potentially vulnerable transitive dependencies or breaking behavior. This is primarily a supply-chain hygiene issue affecting build consistency and trust.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The module docstring presents the skill description entirely in Chinese, which can amount to a language/locale policy issue when no alternative language option or user opt-in is provided. This is a natural-language concern applicable even in code files because it affects how the skill is documented and presented to users.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The module docstring and all user-facing CLI messages are written only in Chinese, indicating the skill's interface is fixed to a specific language. This is a natural-language policy concern because the file does not offer any locale selection, opt-in, or documented justification for restricting output language.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The module's natural-language descriptions are entirely in Chinese, including the top-level docstring and API documentation comments, with no indication that this locale is optional or required for a region-specific purpose. Per the policy, forcing a specific language without opt-in can be a language/locale policy issue.

Static analysis

No suspicious patterns detected.