Back to skill

Security audit

wangkang-skill-c

Security checks for vulnerabilities and agentic risk

Overview

This skill is broadly coherent for creating skills, but its packaging helper can accidentally include files outside the skill directory through symlinks.

Use this only on skill directories you control. Before packaging, check for symlinks and unexpected files, and avoid running the initializer with untrusted skill names or output paths. The behavior does not look deceptive or malicious, but the helper scripts need containment fixes before they are safe for routine use on untrusted inputs.

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

Warning
Location
scripts/init_skill.py:207
Finding
Unvalidated Skill Name Allows Output-Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/init_skill.py`, lines 207 and 285–286 **Vulnerability Type**: Path traversal through an unvalidated directory name **Risk Level**: Medium ### Vulnerable Code ```python # Determine skill directory path skill_dir = Path(path).resolve() / skill_name ``` The command-line input is passed directly to this operation: ```python skill_name = sys.argv[1] path = sys.argv[3] print(f"🚀 Initializing skill: {skill_name}") print(f" Location: {path}") print() result = init_skill(skill_name, path) ``` ### Technical Analysis The command-line help states that a skill name must be a lowercase hyphen-case identifier, but the implementation does not enforce that requirement. The attacker-controlled `skill_name` is appended directly to the resolved base path. A relative name containing parent-directory components, such as `../../new-target`, causes filesystem operations to escape the directory supplied through `--path`. If `skill_name` is an absolute path, `pathlib` discards the preceding base path and uses the absolute path directly. After constructing the unsafe path, the initializer creates the directory and writes generated files under it, including: - `SKILL.md` - `scripts/example.py` - `references/api_reference.md` - `assets/example_asset.txt` The script refuses to proceed when the resulting directory already exists, so this flaw does not directly overwrite an existing target directory. It nevertheless permits creation of a new directory and files outside the intended output boundary wherever the caller has write permission. ### Attack Path 1. An attacker or untrusted automation supplies a crafted skill name: ```bash python scripts/init_skill.py ../../outside-target --path /tmp/allowed ``` 2. The program constructs `/tmp/allowed/../../outside-target` without validating the skill name or checking the final resolved destination. 3. The operating system resolves the parent-directory components, ...[truncated 996 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the documented naming convention before performing any filesystem operation: ```python import re SKILL_NAME_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") if not SKILL_NAME_PATTERN.fullmatch(skill_name): raise ValueError( "Skill name must contain only lowercase letters, digits, " "and single separating hyphens" ) ``` 2. Explicitly reject absolute paths and path separators: ```python candidate_name = Path(skill_name) if candidate_name.is_absolute() or len(candidate_name.parts) != 1: raise ValueError("Skill name must be a single relative path component") ``` 3. Resolve and verify the final destination remains beneath the intended base directory: ```python base_dir = Path(path).resolve() skill_dir = (base_dir / skill_name).resolve() try: skill_dir.relative_to(base_dir) except ValueError: raise ValueError("Resolved skill directory escapes the output directory") ``` 4. Apply validation inside `init_skill()` rather than only in the CLI wrapper so programmatic callers receive the same protection. 5. Add regression tests for `../target`, `../../target`, absolute paths, path separators, empty names, consecutive hyphens, and valid hyphen-case names. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/package_skill.py:70
Finding
Skill Packaging Dereferences Symlinks and Can Disclose External Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/package_skill.py`, lines 70–74 **Vulnerability Type**: Symlink-based arbitrary local file inclusion **Risk Level**: Medium ### Vulnerable Code ```python # Walk through the skill directory for file_path in skill_path.rglob('*'): if file_path.is_file(): # Calculate the relative path within the zip arcname = file_path.relative_to(skill_path.parent) zipf.write(file_path, arcname) print(f" Added: {arcname}") ``` ### Technical Analysis The packager recursively discovers paths and checks them with `Path.is_file()`. This check follows symbolic links. Therefore, a symbolic link located inside the Skill directory and pointing to a regular file outside that directory is accepted as a file. `ZipFile.write()` then opens the path and archives the linked target's contents. The archive name is calculated from the lexical symlink path rather than the resolved target, so the resulting package appears to contain an ordinary file within the Skill while its contents actually originate from outside the Skill directory. The validator only checks `SKILL.md` metadata and does not reject symlinks or verify that every packaged file resolves beneath the Skill root. ### Attack Path 1. An attacker supplies or modifies a Skill directory and adds a symbolic link to a readable external file: ```bash ln -s "$HOME/.ssh/id_rsa" example-skill/assets/reference.txt ``` 2. The victim runs the documented packaging operation: ```bash python scripts/package_skill.py example-skill ./dist ``` 3. `rglob('*')` discovers `assets/reference.txt`. 4. `is_file()` follows the symbolic link and returns true because its target is a regular file. 5. `zipf.write()` reads the external target and stores its contents in the `.skill` archive under the apparently internal path `example-skill/assets/reference.txt`. 6. The victim shares or publishes the resulting archive, disclosing the external file ...[truncated 621 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject symbolic links explicitly before checking the file type: ```python for file_path in skill_path.rglob("*"): if file_path.is_symlink(): print(f"❌ Error: Symbolic links are not permitted: {file_path}") return None if not file_path.is_file(): continue ``` 2. Resolve every candidate and verify that it remains beneath the resolved Skill root: ```python root = skill_path.resolve() for file_path in skill_path.rglob("*"): if file_path.is_symlink() or not file_path.is_file(): continue resolved_file = file_path.resolve(strict=True) try: resolved_file.relative_to(root) except ValueError: raise ValueError(f"File escapes skill directory: {file_path}") ``` 3. Derive archive names only after containment validation and relative to the validated root: ```python arcname = Path(skill_path.name) / resolved_file.relative_to(root) ``` 4. Extend `quick_validate.py` to reject symlinks, broken links, special files, and any resource whose resolved path is outside the Skill root. 5. Consider packaging from an explicit allowlist of regular files and directories rather than archiving every recursively discovered path. 6. Add tests covering symlinks to external files, symlinks to internal files, broken symlinks, named pipes, device files, and ordinary nested resource files. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose does not fully reflect that the skill acts like an operational CLI workflow over local directories and imposes concrete validation and packaging behavior. That hidden capability increases the risk of over-broad invocation and unintended changes to the local workspace.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose does not fully reflect that the skill acts like an operational CLI workflow over local directories and imposes concrete validation and packaging behavior. That hidden capability increases the risk of over-broad invocation and unintended changes to the local workspace.

Self-Modification

High
Category
Rogue Agent
Content
1. Understand the skill with concrete examples
2. Plan reusable skill contents (scripts, references, assets)
3. Initialize the skill (run init_skill.py)
4. Edit the skill (implement resources and write SKILL.md)
5. Package the skill (run package_skill.py)
6. Iterate based on real usage
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
Any example files and directories not needed for the skill should be deleted. The initialization script creates example files in `scripts/`, `references/`, and `assets/` to demonstrate structure, but most skills won't need all of them.

#### Update SKILL.md

**Writing Guidelines:** Always use imperative/infinitive form.
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
# Print next steps
    print(f"\n✅ Skill '{skill_name}' initialized successfully at {skill_dir}")
    print("\nNext steps:")
    print("1. Edit SKILL.md to complete the TODO items and update the description")
    print("2. Customize or delete the example files in scripts/, references/, and assets/")
    print("3. Run the validator when ready to check the skill structure")
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill instructs the agent to run local scripts, create directories, edit files, and package archives, which implies shell, file read, and file write capabilities, yet the skill declares no explicit tool scope. This is dangerous because a broadly-triggered skill can cause an agent to exercise more authority than the metadata communicates, reducing reviewability and increasing the chance of unintended filesystem actions.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The activation condition is extremely broad: any request to create or update a skill may trigger this skill. Because the skill includes instructions to create, edit, delete, validate, and package files, broad triggering raises the chance that it activates in situations where the user wanted discussion or planning rather than workspace modification.

Static analysis

No suspicious patterns detected.