Back to skill

Security audit

Skill Exporter

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent exporter purpose, but its generated service can be compromised by malicious source-skill metadata and it can delete source scripts when source and output paths overlap.

Review this skill before installing. Use it only on skills you trust, choose an output directory outside the source skill directory, inspect generated api.py before running or deploying it, avoid adding production API keys until the generated code is reviewed, and pin dependencies for production builds.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/export.py:421
Finding
Untrusted Skill Metadata Can Inject Python Code into the Generated Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export.py:280-287, 421-426` **Vulnerability Type**: Untrusted data interpolation into generated source code **Risk Level**: High ### Vulnerable Code ```python name = name_match.group(1).strip() if name_match else skill_path.name description = desc_match.group(1).strip() if desc_match else "Exported skill service" return { "name": name, "description": description, "content": content } ``` ```python api_content = TEMPLATES["api.py"].format( skill_name=skill_info["name"], skill_description=skill_info["description"], endpoints=endpoints ) (output_path / "api.py").write_text(api_content) ``` The affected template inserts these values directly into Python string literals: ```python app = FastAPI( title="{skill_name} API", description="{skill_description}", version="1.0.0" ) ``` ### Technical Analysis The `name` and `description` fields are extracted from a source Skill's `SKILL.md` frontmatter using regular expressions. Their contents are not parsed with a structured YAML parser, constrained to a safe character set, or encoded as Python string literals before being inserted into the generated `api.py`. An attacker-controlled value containing quotation marks, newlines, and Python syntax can terminate the intended string literal and introduce arbitrary Python statements. The resulting payload executes when the generated application imports `api.py`, including when Uvicorn starts the exported service. This is a source-code generation injection vulnerability. Merely using `str.format()` does not escape data for the Python syntax context in which it is inserted. ### Attack Path 1. An attacker creates or supplies a source Skill containing a crafted `SKILL.md`. 2. The frontmatter's `name` or `description` field contains content designed to escape the generated Python string literal. 3. A user invokes the exporter with the malicious Skill as `--skill`. 4. `parse_sk ...[truncated 1134 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse frontmatter with a maintained YAML parser rather than regular expressions. 2. Validate the Skill name against a strict allowlist suitable for service names, such as: ```python if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_-]{0,63}", name): raise ValueError("Invalid skill name") ``` 3. Encode metadata as valid Python literals before template insertion: ```python api_content = TEMPLATES["api.py"].format( skill_name_literal=repr(skill_info["name"]), skill_description_literal=repr(skill_info["description"]), endpoints=endpoints, ) ``` The template should use the literals without adding another pair of quotation marks: ```python app = FastAPI( title={skill_name_literal}, description={skill_description_literal}, version="1.0.0" ) ``` 4. Prefer generating a separate JSON metadata file and loading it at runtime rather than embedding untrusted metadata in executable source code. 5. Compile the generated file with `python -m py_compile api.py` before reporting a successful export. 6. Add regression tests containing quotes, backslashes, multiline values, braces, and attempted Python statements. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export.py:409
Finding
Overlapping Source and Output Paths Can Cause Recursive Data Deletion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export.py:409-415` **Vulnerability Type**: Unsafe recursive deletion caused by insufficient path validation **Risk Level**: Medium ### Vulnerable Code ```python # Copy scripts scripts_src = skill_path / "scripts" scripts_dst = output_path / "scripts" if scripts_src.exists(): if scripts_dst.exists(): shutil.rmtree(scripts_dst) shutil.copytree(scripts_src, scripts_dst) print(f" ✓ Copied scripts/") ``` The caller accepts resolved source and destination paths without checking whether they are equal or overlap: ```python skill_path = Path(args.skill).expanduser().resolve() if args.output: output_path = Path(args.output).expanduser().resolve() ``` ### Technical Analysis Before copying source scripts, the exporter unconditionally removes an existing destination `scripts` directory with `shutil.rmtree()`. It does not verify that the output directory is separate from the source Skill. If `output_path` equals `skill_path`, then `scripts_dst` and `scripts_src` refer to the same directory. The exporter recursively deletes the source scripts and then attempts to copy from the directory it just removed. This leads to irreversible local data loss and a failed export. Overlapping paths can also produce unsafe or inconsistent behavior. The use of resolved paths makes reliable equality and ancestry checks possible, but those checks are not currently performed. ### Attack Path 1. A user invokes the exporter with the same directory for `--skill` and `--output`, either accidentally or based on unsafe instructions. 2. Both paths are resolved to the same filesystem location. 3. `scripts_src` and `scripts_dst` consequently identify the same `scripts/` directory. 4. Because that directory exists, `shutil.rmtree(scripts_dst)` recursively deletes it. 5. `shutil.copytree(scripts_src, scripts_dst)` then fails because the source no longer exists. 6. The source Skill is left without its scrip ...[truncated 663 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject identical source and output paths before creating or deleting files: ```python if output_path == skill_path: raise ValueError("Output directory must differ from the source skill directory") ``` 2. Reject overlapping paths in both directions: ```python if output_path.is_relative_to(skill_path) or skill_path.is_relative_to(output_path): raise ValueError("Source and output directories must not overlap") ``` 3. Build the export in a newly created temporary directory outside the source tree. 4. Validate all generated files in the temporary directory and then use an atomic rename to move the completed export into place. 5. Do not recursively delete existing output without explicit user authorization. Prefer failing safely unless an explicit `--force` option is supplied. 6. When `--force` is used, verify the destination against protected paths and optionally create a backup before replacement. 7. Add tests for equal paths, parent-child paths, symlink-resolved paths, and pre-existing destinations. ]]>

T08 · Insecure Dependencies

Note
Location
scripts/export.py:138
Finding
Generated Deployments Use Unpinned Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export.py:138-143, 450-457` **Vulnerability Type**: Non-reproducible dependency resolution and supply-chain exposure **Risk Level**: Low ### Vulnerable Code ```python "requirements.txt": '''fastapi>=0.109.0 uvicorn[standard]>=0.27.0 python-dotenv>=1.0.0 requests>=2.31.0 pydantic>=2.5.0 {llm_deps} ''', ``` Optional LLM dependencies are likewise specified only with minimum versions: ```python if llm == "anthropic": (output_path / "llm_client.py").write_text(TEMPLATES["llm_client_anthropic.py"]) llm_deps = "anthropic>=0.18.0" deps["env_vars"].append("ANTHROPIC_API_KEY") elif llm == "openai": (output_path / "llm_client.py").write_text(TEMPLATES["llm_client_openai.py"]) llm_deps = "openai>=1.12.0" deps["env_vars"].append("OPENAI_API_KEY") ``` The generated Dockerfile installs these dependencies directly: ```dockerfile COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt ``` ### Technical Analysis All generated Python dependencies use lower-bound constraints rather than exact, reviewed versions. Consequently, identical source exports can resolve to different dependency versions depending on the build date and package index state. This does not prove that any currently referenced package is malicious. The weakness is that future, compromised, or incompatible releases satisfying the broad constraints can automatically enter generated production images without a source-code change or explicit review. No hashes are used to verify downloaded artifacts. ### Attack Path 1. A user exports a Skill and receives a generated `requirements.txt` containing broad `>=` constraints. 2. The generated container is built at a later date. 3. Pip resolves the newest versions satisfying those constraints. 4. A dependency release may contain a security regression, compromised code, or an incompatible behavioral change. 5. The selected package is installed into the image w ...[truncated 749 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a lock file containing exact, reviewed dependency versions. 2. Use hashes for artifact integrity, for example through `pip-compile --generate-hashes`. 3. Install in deployment builds with strict hash enforcement: ```dockerfile RUN pip install --no-cache-dir --require-hashes -r requirements.txt ``` 4. Establish a controlled process for periodically updating and testing pinned versions. 5. Run dependency vulnerability scanning in continuous integration and container build pipelines. 6. Consider using an approved internal package mirror or index allowlist for production builds. 7. Pin the base container image by digest to improve overall build reproducibility. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Ae1

High
Category
analysis-evasion
Content
- `SKILL.md` with valid frontmatter
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
ports:
      - "{port}:{port}"
    env_file:
      - .env
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:{port}/health"]
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
ports:
      - "{port}:{port}"
    env_file:
      - .env
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:{port}/health"]
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
ports:
      - "{port}:{port}"
    env_file:
      - .env
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:{port}/health"]
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import_matches = re.findall(r'^(?:from|import)\s+(\w+)', content, re.MULTILINE)
            python_imports.update(import_matches)
    
    # Also check .env file if exists
    env_file = skill_path / ".env"
    if env_file.exists():
        for line in env_file.read_text().splitlines():
Confidence
91% confidence
Finding
The exporter reads the source skill's real .env file and extracts variable names from it. While it does not copy values, touching a live secret-bearing file increases secret-handling risk and can normalize operating on plaintext credentials from untrusted skill directories; in adversarial contexts this also lets a crafted skill influence generated secret documentation via its local .env.

Credential Access

High
Category
Privilege Escalation
Content
python_imports.update(import_matches)
    
    # Also check .env file if exists
    env_file = skill_path / ".env"
    if env_file.exists():
        for line in env_file.read_text().splitlines():
            if "=" in line and not line.startswith("#"):
Confidence
91% confidence
Finding
This line is part of the same behavior that loads a source skill's .env file if present. Even though only keys are added to output, scanning secret files in untrusted input expands the attack surface and may unintentionally disclose sensitive variable naming conventions or ingest attacker-controlled content into generated files.

Credential Access

High
Category
Privilege Escalation
Content
print(f"\n✅ Export complete: {output_path}")
    print(f"\nNext steps:")
    print(f"  1. cd {output_path}")
    print(f"  2. cp .env.example .env && edit .env")
    print(f"  3. docker-compose up --build")
    if target == "railway":
        print(f"  4. railway up")
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill describes capabilities that imply broad file, shell, environment, and possible network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates a least-privilege violation: an agent may grant or infer more capability than users expect, increasing the chance of unsafe file operations, command execution, or data exposure during export and deployment workflows.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: skill-exporter
description: Export Clawdbot skills as standalone, deployable microservices. Use when you want to dockerize a skill, deploy it to Railway or Fly.io, or create an independent API service. Generates Dockerfile, FastAPI wrapper, requirements.txt, deployment configs, and optional LLM client integration.
license: MIT
compatibility: Requires python3. Works with any AgentSkills-compatible agent.
metadata:
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
for dep in deps:
        apt_packages.append(dep)
    
    return f"RUN apt-get update && apt-get install -y --no-install-recommends \\\n    {' '.join(apt_packages)} \\\n    && rm -rf /var/lib/apt/lists/*"


def export_skill(
Confidence
15% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
for dep in deps:
        apt_packages.append(dep)
    
    return f"RUN apt-get update && apt-get install -y --no-install-recommends \\\n    {' '.join(apt_packages)} \\\n    && rm -rf /var/lib/apt/lists/*"


def export_skill(
Confidence
15% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
for dep in deps:
        apt_packages.append(dep)
    
    return f"RUN apt-get update && apt-get install -y --no-install-recommends \\\n    {' '.join(apt_packages)} \\\n    && rm -rf /var/lib/apt/lists/*"


def export_skill(
Confidence
15% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Chaining Abuse

Low
Category
Tool Misuse
Content
for dep in deps:
        apt_packages.append(dep)
    
    return f"RUN apt-get update && apt-get install -y --no-install-recommends \\\n    {' '.join(apt_packages)} \\\n    && rm -rf /var/lib/apt/lists/*"


def export_skill(
Confidence
76% confidence
Finding
The Dockerfile RUN line concatenates detected dependency names directly into a shell command using && chaining. Because dependency names originate from scanning untrusted skill script content and are not validated against a strict allowlist at the sink, a future expansion of detection logic or malformed dep token could turn this into command injection in generated build artifacts.

Static analysis

No suspicious patterns detected.