T09 · Insecure Skill Coding Practices
- Location
scripts/package_skill.py:28- Finding
Unrestricted Archive Collection Can Disclose Local Files
- Content
View full analysis
Vulnerability Details
File Location:
scripts/package_skill.py, lines 28-31
Vulnerability Type: Unsafe archive creation and unintended file disclosure
Risk Level: MediumVulnerable Code
python with zipfile.ZipFile(out_file, "w", zipfile.ZIP_DEFLATED) as zf: for path in skill_dir.rglob("*"): if path.is_file(): arcname = f"{skill_name}/{path.relative_to(skill_dir)}" zf.write(path, arcname)Technical Analysis
The packaging function recursively archives every file found below the supplied Skill directory. It does not:
- Restrict packaging to an explicit allowlist.
- Exclude secret-bearing files such as
.envor private configuration files. - Exclude VCS metadata, temporary files, build artifacts, or the output directory.
- Reject symbolic links before calling
is_file()andzf.write(). - Resolve each candidate and verify that its target remains within the Skill directory.
Path.is_file()follows symbolic links. Consequently, a symbolic link located inside the Skill directory can point to an arbitrary readable file elsewhere on the host, andZipFile.write()can copy the target's contents into the generated archive. The archive entry retains the in-tree symbolic-link path, making the external origin of the packaged content non-obvious.If
--outpoints to a directory insideskill_dir, the archive currently being generated may also be encountered by the recursive traversal. This can produce malformed, unexpectedly large, or non-deterministic artifacts.Attack Path
- An attacker gains the ability to contribute files to the Skill directory, such as through a malicious pull request or compromised source archive.
- The attacker adds a symbolic link beneath that directory pointing to a predictable sensitive file readable by the packaging user, or adds secret-bearing files that should not be distributed.
- A maintainer runs
scripts/package_skill.pywithout reviewing every recurs ...[truncated 1011 chars]
- Remediation
View remediation
Remediation Suggestions
-
Use an explicit packaging allowlist. Package only known distributable files and directories, such as
SKILL.md, approved files underreferences/, and approved scripts underscripts/. -
Reject symbolic links explicitly.
python if path.is_symlink(): raise ValueError(f"Symbolic links are not allowed: {path}")- Enforce containment after resolution. Resolve every candidate and verify that it remains beneath the resolved Skill root before reading it.
python skill_root = skill_dir.resolve() resolved = path.resolve(strict=True) if not resolved.is_relative_to(skill_root): raise ValueError(f"Path escapes skill directory: {path}")-
Exclude sensitive and generated content. At minimum, reject
.env*, credential and key files,.git, caches, temporary files, build outputs, and distribution directories. -
Require the output directory to be outside the Skill directory. Resolve both paths and abort if the output directory equals or is nested beneath
skill_dir. -
Precompute the file manifest before opening the output archive. Validate the complete manifest first, then create the archive. This prevents the newly created archive from entering an active recursive traversal.
-
Log and review the manifest. Print every included relative path and optionally require confirmation before creating a release artifact.
-
Add regression tests covering external symbolic links, internal symbolic links,
.envexclusion, nested output directories, and valid allowlisted packaging.
-
