T08 · Insecure Dependencies
- Location
scripts/find_skills.py:171- Finding
Unpinned Third-Party CLI Execution and Unverified Skill Installation Sources
- Content
View full analysis
Vulnerability Details
File Location:
scripts/find_skills.py:131-150,scripts/find_skills.py:171-208,scripts/find_skills.py:279-287; related documentation atSKILL.md:86-93and default source atreferences/catalog.json:4
Vulnerability Type: Supply-chain exposure through unpinned executable dependencies and mutable installation sources
Risk Level: HighVulnerable Code
The status and verification paths execute the unpinned
skillsnpm package throughnpx:python def installed_skill_names( catalog: dict[str, Any], agent: str | None, scope: str ) -> set[str]: command = ["npx", "--yes", "skills", "list"] if scope == "global": command.append("--global") if agent: command.extend(["--agent", agent]) completed, payload = run_json([*command, "--json"]) if completed.returncode != 0: detail = completed.stderr.strip() or completed.stdout.strip() fail(f"unable to list installed skills: {detail}", completed.returncode) if isinstance(payload, list): return { item.get("name") for item in payload if isinstance(item, dict) and isinstance(item.get("name"), str) } completed = subprocess.run(command, capture_output=True, text=True, check=False)The installation path similarly invokes the unpinned package, accepts a caller-supplied source, defaults to global installation, and verifies only installed names:
python def install_skills_cli( catalog: dict[str, Any], records: list[dict[str, Any]], agent: str | None, scope: str, source: str | None, dry_run: bool, as_json: bool, ) -> None: skill_names = [record["name"] for record in records] package_source = source or catalog["repository"] command = [ "npx", "--yes", "skills", "add", package_source, ] ...[truncated 4445 chars]- Remediation
View remediation
Remediation Suggestions
- Pin the npm executable to an audited exact version, for example by invoking a fixed
skills@X.Y.Zrelease. - Use npm lockfiles and integrity metadata where possible. In controlled deployments, install dependencies ahead of time with a reproducible package manager command and invoke the pinned local binary rather than dynamically resolving it through
npx. - Pin the official Skill repository to a reviewed immutable commit SHA or signed release instead of a mutable repository reference.
- Restrict
--sourceto reviewed local directories or an explicit allowlist of trusted repositories. For remote sources, require immutable revisions and validate repository URL schemes and ownership. - Require explicit user confirmation before any network retrieval or global installation, including a clear display of the exact package version, source, revision, target scope, and destination.
- Change the default installation scope from
globaltoprojectto reduce cross-project and cross-session impact. - Verify installed file hashes, source provenance, and expected paths against a signed manifest. Do not treat the presence of a Skill name in
skills listas sufficient integrity verification. - Consider running retrieval and installation in a sandbox with minimal filesystem, environment-variable, credential, and network access.
- Fail closed when provenance or integrity validation cannot be completed.
- Pin the npm executable to an audited exact version, for example by invoking a fixed
