Back to skill

Security audit

clawhub

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent skill-authoring helper, but its packaging script can accidentally include files outside the skill directory through symlinks.

Review before installing if you may package skill folders from untrusted sources. Do not run the packager on directories you did not inspect for symlinks, and do not pass untrusted skill names or output paths to the initializer. There is no evidence of hidden network exfiltration or destructive behavior.

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/package_skill.py:70
Finding

Symbolic Links Can Include Files Outside the Skill Directory in Packaged Archives

Content
View full analysis

Vulnerability Details

File Location: scripts/package_skill.py, lines 70–75
Vulnerability Type: Unrestricted symbolic-link traversal during archive creation
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 packaging routine recursively processes every file under the supplied Skill directory. Path.is_file() follows symbolic links, while zipfile.ZipFile.write() reads the content of the link target.

The archive name is calculated from the symbolic link's lexical location rather than from the resolved target. Consequently, a symbolic link that appears to be inside the Skill can point to a file outside it, and the external file's contents will be stored in the generated archive under an innocuous internal path.

The validator does not inspect packaged resources for symbolic links or verify that their resolved paths remain within the Skill root.

Attack Path

  1. An attacker creates or distributes a Skill directory containing a symbolic link, such as assets/host-data, that points to a sensitive local file.
  2. A user invokes scripts/package_skill.py on that directory.
  3. Recursive traversal discovers the symbolic link.
  4. is_file() reports that the link target is a file.
  5. zipf.write() follows the link and reads the external target.
  6. The external file is embedded in the generated .skill archive.
  7. If the archive is shared or published, the captured local information is disclosed.

Impact Assessment

The vulnerability allows disclosure of files readable by the account running the packaging script. Depending on the host environment, exposed data could inclu ...[truncated 354 chars]

Remediation
View remediation

Remediation Suggestions

  • Reject symbolic links explicitly before adding files to the archive:

    python
    if file_path.is_symlink():
        raise ValueError(f"Symbolic links are not permitted: {file_path}")
    
  • Resolve every candidate path and verify that it remains beneath the resolved Skill root:

    python
    skill_root = skill_path.resolve()
    
    for file_path in skill_root.rglob("*"):
        if file_path.is_symlink():
            raise ValueError(f"Symbolic links are not permitted: {file_path}")
    
        resolved_file = file_path.resolve(strict=True)
        if not resolved_file.is_relative_to(skill_root):
            raise ValueError(f"File escapes the skill directory: {file_path}")
    
        if resolved_file.is_file():
            arcname = resolved_file.relative_to(skill_root.parent)
            zipf.write(resolved_file, arcname)
    
  • Apply the same containment validation to SKILL.md and all resources before packaging.

  • Consider using an explicit resource allowlist and excluding sensitive filenames such as environment files, private keys, and credential stores.

  • Add automated tests covering file symlinks, directory symlinks, broken links, and links targeting files outside the Skill root.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/init_skill.py:195
Finding

Unvalidated Skill Name Permits Creation Outside the Requested Output Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/init_skill.py, lines 195–206 and 277–289
Vulnerability Type: Path traversal through an unvalidated directory name
Risk Level: Low

Vulnerable Code

python
def init_skill(skill_name, path):
    """
    Initialize a new skill directory with template SKILL.md.

    Args:
        skill_name: Name of the skill
        path: Path where the skill directory should be created

    Returns:
        Path to created skill directory, or None if error
    """
    # Determine skill directory path
    skill_dir = Path(path).resolve() / skill_name

    # Check if directory already exists
    if skill_dir.exists():
        print(f"❌ Error: Skill directory already exists: {skill_dir}")
        return None

    # Create skill directory
    try:
        skill_dir.mkdir(parents=True, exist_ok=False)

The name and path are taken directly from command-line arguments:

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 Skill names must use lowercase hyphen-case, but the implementation does not enforce that restriction. The untrusted skill_name value is appended directly to the resolved base path.

A name containing parent-directory components can escape the intended base directory. An absolute skill_name can also cause pathlib path composition to discard the preceding base path. Subsequent operations create the destination directory, write SKILL.md and resource files, and mark the generated example script executable.

The existing-directory check and exist_ok=False prevent straightforward overwriting of an existing target directory, reducing but not eliminating the filesystem-boundary violation.

Attack Path

  1. A wrapper, automation serv ...[truncated 1050 chars]
Remediation
View remediation

Remediation Suggestions

  • Enforce the documented Skill-name syntax before performing filesystem operations:

    python
    import re
    
    if not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", skill_name):
        raise ValueError(
            "Skill name must contain only lowercase letters, digits, "
            "and single hyphens"
        )
    
  • Reject absolute paths, path separators, . components, and .. components in skill_name.

  • Resolve both the base directory and final destination, then enforce containment:

    python
    base_dir = Path(path).resolve()
    skill_dir = (base_dir / skill_name).resolve()
    
    if not skill_dir.is_relative_to(base_dir):
        raise ValueError("Skill directory must remain inside the output directory")
    
  • Apply a consistent maximum length. The initializer documents a 40-character limit while the validator permits up to 64 characters; these rules should be aligned.

  • Add tests for absolute paths, parent traversal, embedded separators, empty names, leading or trailing hyphens, and consecutive hyphens.

  • If the initializer is exposed through a service, perform validation at both the service boundary and within this script.

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
98% confidence
Finding

The declared purpose describes an instructional or guidance-oriented skill for creating or updating skills. The supplied code does not provide guidance content or interactive assistance; instead, it implements a packaging tool that validates a skill directory and writes a distributable .skill file to disk. This is a materially different primary purpose and includes undeclared file access and archive creation capabilities. While related to skills generally, the behavior is operational tooling rather than a guide for authoring skills.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description says this is a guide for creating effective skills, implying instructional or advisory content for authors. The supplied code instead implements a command-line validator that reads files from disk and enforces specific SKILL.md schema and formatting rules. While validation could support skill creation, the code's primary function is automated checking/linting, not guiding users through creation or update workflows. No dangerous undeclared behavior is evident, but the purpose is materially different from the declared description.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
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.

Content

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

md
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

Self-Modification

High
Category
Rogue Agent
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.

Content

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

md
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.

Self-Modification

High
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/init_skill.py (reported line 266)May include surrounding context.

python
# 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")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description says the skill should be used when users want to create a new skill or update an existing skill, which is a wide activation scope without concrete trigger boundaries or exclusion cases. Because this frontmatter is the primary trigger mechanism, the lack of narrower context or negative examples could cause unintended invocation for general discussion about skills.

Content

No source excerpt is available for this finding.