Back to skill

Security audit

Skill Polisher

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local feedback and quality tracker, but its scripts can write persistent records outside the advertised storage area when given crafted skill names.

Review before installing. This skill stores ratings, comments, tracking history, and success criteria under ~/.openclaw/workspace/.skill-polisher/. Use only trusted skill names, avoid passing paths or names containing slashes or '..', and treat stored feedback comments as private local data until path validation, file permissions, and deletion controls are improved.

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/set-expectation.py:13
Finding
Unvalidated Skill Names Allow Filesystem Path Traversal and Out-of-Scope Writes## Vulnerability Details **File Location**: `scripts/collect-feedback.py:18-23, 53-66`; `scripts/set-expectation.py:13-18, 31-38` **Vulnerability Type**: Filesystem path traversal and arbitrary file write **Risk Level**: High ### Vulnerable Code `scripts/collect-feedback.py:18-23, 53-66` ```python def get_feedback_dir(skill_name: str) -> Path: """Get the feedback storage directory for a Skill.""" base_dir = Path.home() / ".openclaw/workspace/.skill-polisher/feedback" skill_dir = base_dir / skill_name skill_dir.mkdir(parents=True, exist_ok=True) return skill_dir def save_feedback(feedback: dict) -> Path: """Save feedback to a file.""" skill_name = feedback["skill"] feedback_dir = get_feedback_dir(skill_name) # Generate the filename timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") filename = f"{timestamp}.json" filepath = feedback_dir / filename with open(filepath, "w", encoding="utf-8") as f: json.dump(feedback, f, ensure_ascii=False, indent=2) return filepath ``` `scripts/set-expectation.py:13-18, 31-38` ```python def get_expectation_path(skill_name: str) -> Path: """Get the Skill expectation file path.""" base_dir = Path.home() / ".openclaw/workspace/.skill-polisher/expectations" base_dir.mkdir(parents=True, exist_ok=True) return base_dir / f"{skill_name}.json" def save_expectation(skill_name: str, expectation: dict): """Save Skill expectations.""" path = get_expectation_path(skill_name) expectation["skill"] = skill_name expectation["updated_at"] = datetime.now().isoformat() with open(path, "w", encoding="utf-8") as f: json.dump(expectation, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis User-controlled Skill names are incorporated directly into filesystem paths without validating their syntax or confirming that the resolved destination remain ...[truncated 2791 chars]
Remediation
## Remediation Suggestions 1. Apply one centralized validator to every externally supplied or persisted Skill name: ```python import re SKILL_NAME_RE = re.compile(r"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$") def validate_skill_name(value: str) -> str: if not SKILL_NAME_RE.fullmatch(value): raise ValueError("Invalid Skill name") return value ``` 2. Explicitly reject absolute paths, path separators, empty values, `.` components, and `..` components. 3. Resolve and verify every destination before accessing it: ```python base = ( Path.home() / ".openclaw/workspace/.skill-polisher/expectations" ).resolve() destination = (base / f"{validate_skill_name(skill_name)}.json").resolve() if destination.parent != base: raise ValueError("Expectation path escapes its storage directory") ``` 4. Apply equivalent confinement checks to feedback directories and all read paths in `check-spec.py`, `health-report.py`, `polish-suggest.py`, and `tracking.py`. 5. Remove `--force` if it is unnecessary. Otherwise, ensure it bypasses only tracking membership and never bypasses name validation or path confinement. 6. Consider rejecting symbolic-link destinations and using secure atomic file replacement to reduce symlink and race-condition risks.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/collect-feedback.py:18
Finding
Private Feedback and Tracking Data Are Written Without Explicit Restrictive Permissions## Vulnerability Details **File Location**: `scripts/collect-feedback.py:18-23, 53-66`; `scripts/set-expectation.py:13-18, 31-38`; `scripts/tracking.py:13-16, 29-33` **Vulnerability Type**: Insecure permissions for plaintext sensitive data **Risk Level**: Medium ### Vulnerable Code `scripts/collect-feedback.py:18-23, 53-66` ```python def get_feedback_dir(skill_name: str) -> Path: """Get the feedback storage directory for a Skill.""" base_dir = Path.home() / ".openclaw/workspace/.skill-polisher/feedback" skill_dir = base_dir / skill_name skill_dir.mkdir(parents=True, exist_ok=True) return skill_dir def save_feedback(feedback: dict) -> Path: """Save feedback to a file.""" skill_name = feedback["skill"] feedback_dir = get_feedback_dir(skill_name) # Generate the filename timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") filename = f"{timestamp}.json" filepath = feedback_dir / filename with open(filepath, "w", encoding="utf-8") as f: json.dump(feedback, f, ensure_ascii=False, indent=2) return filepath ``` `scripts/tracking.py:13-16, 29-33` ```python def get_tracking_file() -> Path: """Get the tracking-list file path.""" tracking_file = Path.home() / ".openclaw/workspace/.skill-polisher/tracking.json" tracking_file.parent.mkdir(parents=True, exist_ok=True) return tracking_file def save_tracking(data: dict): """Save the tracking list.""" tracking_file = get_tracking_file() with open(tracking_file, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The Skill explicitly describes feedback, expectations, metrics, and tracking records as private user data. However, its directories are created without an explicit mode, and its files are opened using ordinary `"w"` mode without enforcing owner-only access. Effective permissions ...[truncated 1716 chars]
Remediation
## Remediation Suggestions 1. Create the top-level private data directory with owner-only permissions: ```python base_dir.mkdir(parents=True, exist_ok=True, mode=0o700) base_dir.chmod(0o700) ``` 2. Create all feedback and expectation subdirectories with mode `0700`. 3. Create files using an owner-only mode such as `0600`, rather than relying solely on the ambient umask: ```python import os fd = os.open( filepath, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(feedback, f, ensure_ascii=False, indent=2) ``` 4. For files that must be replaced, write to a securely created temporary file in the same private directory, flush and synchronize it, set mode `0600`, and atomically replace the destination. 5. Reject symbolic links and inspect destination ownership before overwriting an existing file. 6. On startup, audit and repair the permissions of existing `.skill-polisher` directories and files. 7. Document that feedback comments may contain sensitive project information and provide retention and deletion controls.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Maintaining tracking.json and other local state directly contradicts the repeated '只读分析' framing. The danger here is less remote code execution and more operator deception: misleading capability claims can bypass scrutiny, obscure audit trails, and normalize silent collection of workflow metadata across other skills.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Maintaining tracking.json and other local state directly contradicts the repeated '只读分析' framing. The danger here is less remote code execution and more operator deception: misleading capability claims can bypass scrutiny, obscure audit trails, and normalize silent collection of workflow metadata across other skills.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Maintaining tracking.json and other local state directly contradicts the repeated '只读分析' framing. The danger here is less remote code execution and more operator deception: misleading capability claims can bypass scrutiny, obscure audit trails, and normalize silent collection of workflow metadata across other skills.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Maintaining tracking.json and other local state directly contradicts the repeated '只读分析' framing. The danger here is less remote code execution and more operator deception: misleading capability claims can bypass scrutiny, obscure audit trails, and normalize silent collection of workflow metadata across other skills.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Maintaining tracking.json and other local state directly contradicts the repeated '只读分析' framing. The danger here is less remote code execution and more operator deception: misleading capability claims can bypass scrutiny, obscure audit trails, and normalize silent collection of workflow metadata across other skills.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata explicitly describes the capability as 'read-only analysis, not modifying any skill files', but this script creates directories and writes feedback records to disk under the user's home directory. That mismatch is security-relevant because users and orchestrators may grant it broader trust or run it in contexts where persistence is unexpected, enabling silent collection of execution telemetry and comments.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script writes JSON expectation files under the user's workspace when run with --auto-setup or --interactive, which directly contradicts the skill's stated 'read-only analysis' behavior. This is dangerous because users or automation may trust the metadata and execute the skill in environments where file mutation is prohibited, leading to integrity issues, unauthorized persistence, or policy bypass.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The function creates a persistent directory under the user's home directory even though the skill is described as read-only analysis that does not modify skill-related files. This is dangerous because it violates operator expectations and enables silent state changes and data retention, which can undermine trust, compliance assumptions, and workflow safety in environments that rely on read-only behavior.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This code writes and updates expectation records on disk, directly conflicting with the stated read-only nature of the skill. Such hidden persistence is risky because users or orchestration systems may invoke the skill assuming no side effects, leading to unauthorized local state changes and accumulation of potentially sensitive operational data.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script creates and writes ~/.openclaw/workspace/.skill-polisher/tracking.json, mutating persistent state even though the skill metadata claims '只读分析,不修改任何技能文件'. This is a genuine integrity/trust issue because operators may grant or run the skill under a read-only assumption, and the mismatch can bypass policy expectations even if it does not directly modify tracked skill source files.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The manifest description is entirely in Chinese and presents the skill's usage conditions and behavior in that language only, without any indication that the user can choose another language. This creates a language/locale policy concern because the skill appears to prescribe a specific language experience without opt-in or justification.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The best-practices document recommends adding automatic feedback collection by invoking a separate script via subprocess after every skill execution, but it provides no user-facing notice, consent step, or explanation of what data is collected. In a skill ecosystem, normalizing silent post-execution telemetry increases privacy risk and can lead downstream skill authors to implement undisclosed data collection behavior.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This markdown file includes a prescriptive instruction to '输出中文错误信息', which imposes a specific language choice. Under the policy, locale or language constraints should not be forced unless the user is given an opt-in choice or the constraint is clearly justified as region-specific.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This markdown file is entirely written in Chinese and specifically requires the `description` field to use the Chinese format `"当...时使用"`. That imposes a specific language/locale convention without any user opt-in or documented region-specific justification, which matches the natural-language locale policy violation criteria.

Intent-Code Divergence

Medium
Confidence
86% confidence
Finding
The module documentation presents the script as merely collecting feedback, but the implementation also creates storage directories and persists structured records. This incomplete disclosure can mislead reviewers and users about side effects, reducing their ability to assess privacy and data-handling risk, especially because comments may contain sensitive information.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code file contains natural-language strings and documentation that force a specific language for usage and prompts. Under the policy, locale-specific language is a violation when users are not given an opt-in choice and no region-specific justification is provided.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This Python file includes its top-level docstring and most user-facing CLI prompts entirely in Chinese, which imposes a specific language on users without any opt-in or alternative locale support. The policy explicitly calls out language or locale constraints as findings when the skill does not offer user choice or justify a region-specific limitation.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This Python file is in scope for SQP-3 because policy violations can appear in docstrings, help text, and printed strings. The script's user-facing description, CLI help, and report output are consistently Chinese-only, with no opt-in or alternative locale, which can violate a language/locale policy that requires user choice.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The module docstring and all user-facing CLI descriptions are written only in Chinese, and the script prints Chinese-only output throughout. For a general-purpose skill utility, this imposes a language choice on users without opt-in or justification, which matches the language/locale policy violation criteria.

Intent-Code Divergence

Medium
Confidence
85% confidence
Finding
The module description frames the tool as managing expectations but does not clearly disclose that it creates and updates persistent files. In the context of a skill ecosystem that claims read-only behavior, incomplete disclosure is dangerous because it can mislead reviewers and users about side effects, increasing the chance of unapproved data writes.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains natural-language docstrings and CLI guidance exclusively in Chinese, which effectively forces a specific language on users. The file does not provide any opt-in, alternative locale, or justification that the tool is intended only for a Chinese-speaking environment.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The interactive editing and display functions print all prompts, labels, and status messages in Chinese only. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless the locale restriction is clearly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The argparse description and help text are presented only in Chinese, which can prevent users in other locales from understanding how to use the script. No opt-in or documented locale limitation is present to justify the language restriction.

Intent-Code Divergence

Medium
Confidence
88% confidence
Finding
The module help text presents the script as benign tracking management, but in the broader skill context the package is advertised as read-only while the code performs writes. This discrepancy increases the chance of unsafe deployment decisions, because users and reviewers may trust the declared behavior rather than inspect the code paths that create directories and save JSON state.

Static analysis

No suspicious patterns detected.