Back to skill

Security audit

Skill Bundle - Skill 打包管理

Security checks for vulnerabilities and agentic risk

Overview

This bundle-management skill has a coherent purpose, but its installer can copy or delete filesystem paths outside the intended skills directory when given unsafe manifest or registry values.

Review before installing. Only run this skill's installer on trusted bundle manifests, with a non-privileged account, and against a disposable or backed-up skills directory until path validation and uninstall safeguards are fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/bundle-installer.py:173
Finding

Manifest-Controlled Skill Names Enable Arbitrary Directory Deletion During Uninstall

Content
View full analysis

Vulnerability Details

File Location: scripts/bundle-installer.py:130-136 and scripts/bundle-installer.py:173-193
Vulnerability Type: Path traversal leading to arbitrary directory deletion
Risk Level: High

Vulnerable Code

python
for skill in skills:
    skill_name = skill.get("name", "")
    skill_path = skill.get("path", f"skills/{skill_name}")

    try:
        copy_skill(source_dir, skill_path, target_dir)
        installed_skills.append(skill_name)
    except SystemExit:
        errors.append(skill_name)

The unvalidated names are subsequently saved in the installation registry and consumed during uninstallation:

python
# Delete each skill directory
for skill_name in bundle_info.get("skills", []):
    skill_dir = target_dir / skill_name
    if skill_dir.exists():
        try:
            # Preserve user data directories
            user_data_dirs = [".learnings", ".cache", "user-data"]
            for ud in user_data_dirs:
                ud_path = skill_dir / ud
                if ud_path.exists():
                    # Move to an adjacent backup directory
                    backup_name = f".{skill_name}_{ud}_bak"
                    backup_path = target_dir / backup_name
                    if backup_path.exists():
                        shutil.rmtree(str(backup_path))
                    shutil.move(str(ud_path), str(backup_path))

            shutil.rmtree(str(skill_dir))
            removed_skills.append(skill_name)
        except OSError as e:
            error_exit(f"Failed to delete skill ({skill_dir}): {e}")

Technical Analysis

The name field of every skill is controlled by the bundle manifest. The installer does not require this value to be a simple directory name and does not reject absolute paths, path separators, or .. traversal components.

During installation, the untrusted value is saved in `.installed-bundles.json ...[truncated 1999 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce a strict skill-name allowlist, such as ^[A-Za-z0-9][A-Za-z0-9_-]*$.

  2. Explicitly reject absolute names, path separators, . and .. components, and empty names.

  3. Canonicalize the target root and candidate directory before deletion:

    python
    target_root = target_dir.resolve()
    skill_dir = (target_root / skill_name).resolve()
    
    if not skill_dir.is_relative_to(target_root) or skill_dir == target_root:
        error_exit("Unsafe skill path in installation registry")
    
  4. Store the actual validated installation directory name in the registry rather than the raw manifest field.

  5. Treat .installed-bundles.json as untrusted input and validate all existing registry entries before performing deletion or movement.

  6. Refuse to recursively delete symlinks or paths whose resolved targets escape the target root.

  7. Perform all validation before making any filesystem changes, and abort the complete operation if any entry is unsafe.

  8. Add regression tests covering absolute paths, nested traversal, malformed registry entries, separators, symlinks, and names that resolve to the target root itself.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/bundle-installer.py:99
Finding

Manifest Path Traversal Allows Copying Directories Outside the Bundle Source

Content
View full analysis

Vulnerability Details

File Location: scripts/bundle-installer.py:99-114, with untrusted inputs at scripts/bundle-installer.py:130-136 and scripts/bundle-installer.py:267-274
Vulnerability Type: Path traversal and unrestricted local directory copying
Risk Level: High

Vulnerable Code

python
def copy_skill(source_base: Path, skill_path: str, target: Path) -> None:
    """Copy one skill into the target directory."""
    src = source_base / skill_path
    dst = target / src.name

    if not src.is_dir():
        error_exit(f"Skill source directory does not exist: {src}")

    # Back up an existing target first
    if dst.exists():
        backup_dir(dst)
        shutil.rmtree(str(dst))

    try:
        shutil.copytree(str(src), str(dst))
    except OSError as e:
        error_exit(f"Failed to copy skill ({src} to {dst}): {e}")

Installation passes the manifest-controlled path directly to this function:

python
for skill in skills:
    skill_name = skill.get("name", "")
    skill_path = skill.get("path", f"skills/{skill_name}")

    try:
        copy_skill(source_dir, skill_path, target_dir)
        installed_skills.append(skill_name)
    except SystemExit:
        errors.append(skill_name)

Update performs the same unsafe operation:

python
for skill in skills:
    skill_name = skill.get("name", "")
    skill_path = skill.get("path", f"skills/{skill_name}")
    try:
        copy_skill(source_dir, skill_path, target_dir)
        updated_skills.append(skill_name)
    except SystemExit:
        pass

Technical Analysis

The manifest's skill.path field is used as a filesystem path without validation or canonical containment checks:

python
src = source_base / skill_path

An absolute skill_path overrides source_base. A relative path containing .. can traverse outside the bundle source. If the resulting path ref ...[truncated 2553 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject absolute values in skill.path.

  2. Resolve both the source root and candidate source, then require the candidate to remain strictly inside the source root:

    python
    source_root = source_base.resolve()
    candidate = source_base / skill_path
    
    if candidate.is_absolute() and not str(candidate).startswith(str(source_root)):
        error_exit("Absolute skill paths are not allowed")
    
    src = candidate.resolve()
    if not src.is_relative_to(source_root) or src == source_root:
        error_exit("Skill path escapes the bundle source")
    

    The implementation should preferably reject absolute manifest values before joining rather than relying on string comparisons.

  3. Inspect path components and reject .., empty paths, and other ambiguous forms before resolution.

  4. Validate symlinks inside copied directory trees and reject entries whose resolved targets escape the approved source root.

  5. Derive the destination from a separately validated skill name, not from the basename of an attacker-selected source path.

  6. Detect destination collisions before copying and require explicit user authorization instead of automatically replacing existing content.

  7. Require full manifest validation before both installation and update.

  8. Stage all copies in a secure temporary directory under the target filesystem and commit them only after every entry passes validation, providing transactional rollback on failure.

  9. Add tests for absolute paths, traversal paths, symlink escapes, basename collisions, source-root selection, and update operations.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bundle-validator.py:157
Finding

Manifest Validator Does Not Enforce Path, Name, or Field-Type Security Constraints

Content
View full analysis

Vulnerability Details

File Location: scripts/bundle-validator.py:157-191
Vulnerability Type: Insufficient input validation
Risk Level: Medium

Vulnerable Code

python
# 3. Validate each skill
skill_names = set()
for i, skill in enumerate(manifest.get("skills", [])):
    prefix = f"skills[{i}]({skill.get('name', '?')})"

    # Required fields
    for field in SKILL_REQUIRED_FIELDS:
        if field not in skill:
            errors.append(f"{prefix}: Missing required field '{field}'")

    # Version format
    if "version" in skill and not parse_semver(skill["version"]):
        errors.append(f"{prefix}: Invalid version '{skill['version']}'")

    # Path existence
    if args.skills_dir and "path" in skill:
        skill_path = Path(args.skills_dir) / skill["name"]
        if not skill_path.is_dir():
            # Also check the path from the manifest
            alt_path = Path(args.manifest).parent / skill["path"]
            if not alt_path.is_dir():
                warnings.append(f"{prefix}: Skill directory does not exist ({skill['path']})")

        # SKILL.md existence
        skill_md = skill_path / "SKILL.md"
        if not skill_md.exists():
            alt_md = Path(args.manifest).parent / skill["path"] / "SKILL.md"
            if not alt_md.exists():
                warnings.append(f"{prefix}: SKILL.md does not exist")

    # Duplicate name
    name = skill.get("name", "")
    if name in skill_names:
        errors.append(f"{prefix}: Duplicate skill name")
    skill_names.add(name)

Technical Analysis

The validator checks required-field presence, semantic versions, duplicate names, and optional filesystem existence. It does not enforce the documented restriction that bundle and skill names contain only permitted characters, and it does not ensure that skill.path is relative to and contained within the manifest directory.

A traversal or abso ...[truncated 1867 chars]

Remediation
View remediation

Remediation Suggestions

  1. Define and enforce a formal JSON Schema for the entire manifest.
  2. Require the top-level document to be an object, skills to be an array, every skill to be an object, names and paths to be nonempty strings, dependencies to be arrays of strings, and platform values to have the documented types.
  3. Enforce strict patterns for bundle and skill names, such as ^[A-Za-z0-9][A-Za-z0-9_-]*$.
  4. Reject absolute paths, .. components, path separators in names, and any resolved skill path outside the manifest source root.
  5. Treat missing skill directories and missing SKILL.md files as errors rather than warnings when validating an installable bundle.
  6. Validate symlink targets and reject filesystem entries that escape the approved source directory.
  7. Make successful validation mandatory within the install and update code paths instead of relying on users to run a separate command.
  8. Separate pure manifest validation from filesystem validation and return deterministic structured errors without uncaught type exceptions.
  9. Add negative tests for malformed JSON types, unsafe names, absolute paths, traversal paths, symlink escapes, duplicate identities, and missing package files.
Vulnerability Patterns
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill description and operational guidance are written in Chinese, and there is no indication that users may choose another language or that the skill is intentionally region-specific. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

该 markdown 文档全文使用中文撰写,且未说明这是面向特定中文区域/受众的限定文档,也未提供多语言选择或用户 opt-in。根据规则,强制单一语言而无选择或明确地域性正当说明,属于自然语言层面的语言/locale 政策风险。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The uninstall flow deletes installed skill directories with shutil.rmtree after only a brief code comment about preserving some user-data subdirectories. There is no confirmation prompt or explicit user-facing warning before this destructive operation, even though it removes bundle contents from the target skills directory.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The uninstall flow tries to preserve user data by moving directories like .learnings and .cache into a backup path under the target directory, but it first unconditionally deletes any existing backup directory with the same name. That can destroy pre-existing data unrelated to the current uninstall, and because the operation happens automatically during uninstall, users may suffer silent data loss.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file includes user-facing natural-language content such as the module docstring, usage instructions, help text, and status/error messages in Chinese. Under the policy, forcing a specific language without user opt-in is a locale-policy violation unless the constraint is explicitly justified, which is not present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script's natural-language interface, including the module description, usage text, help strings, and result messages, is written exclusively in Chinese. This imposes a specific language on users without any documented opt-in or locale-selection mechanism.

Content

No source excerpt is available for this finding.