Back to skill

Security audit

Volcengine Skills Finder

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it claims, but it can globally install new agent skills using unpinned remote tooling and mutable sources.

Review before installing. Prefer dry-run first, use project scope instead of global where possible, avoid arbitrary --source values, and only install from a reviewed or pinned Volcengine source after explicitly approving the exact skills and target agent.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (1)

T08 · Insecure Dependencies

Error
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 at SKILL.md:86-93 and default source at references/catalog.json:4
Vulnerability Type: Supply-chain exposure through unpinned executable dependencies and mutable installation sources
Risk Level: High

Vulnerable Code

The status and verification paths execute the unpinned skills npm package through npx:

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

  1. Pin the npm executable to an audited exact version, for example by invoking a fixed skills@X.Y.Z release.
  2. 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.
  3. Pin the official Skill repository to a reviewed immutable commit SHA or signed release instead of a mutable repository reference.
  4. Restrict --source to reviewed local directories or an explicit allowlist of trusted repositories. For remote sources, require immutable revisions and validate repository URL schemes and ownership.
  5. 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.
  6. Change the default installation scope from global to project to reduce cross-project and cross-session impact.
  7. Verify installed file hashes, source provenance, and expected paths against a signed manifest. Do not treat the presence of a Skill name in skills list as sufficient integrity verification.
  8. Consider running retrieval and installation in a sandbox with minimal filesystem, environment-variable, credential, and network access.
  9. Fail closed when provenance or integrity validation cannot be completed.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs the agent to read local files and execute shell commands to enumerate and install additional skills, but it does not declare any explicit tool scope such as allowed-tools or permissions. In a marketplace/discovery skill, this is especially risky because it can expand the agent's capabilities and modify the local environment without a clearly bounded permission model, increasing the chance of overbroad command execution or unintended installs.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

Using npx skills without pinning an exact package/version makes execution dependent on whatever package version resolves at runtime, which can change unexpectedly or be replaced through registry or supply-chain compromise. Because this skill performs installation of further skills, an unpinned execution path compounds risk by allowing a malicious or altered CLI version to install unintended content or run arbitrary code during the install flow.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The default prompt uses broad fallback language ('when my current Volcengine capabilities are insufficient') that can activate in many ordinary situations without strong scoping or approval boundaries. Because this skill can list and install additional skills, overly broad invocation increases the chance of unnecessary tool discovery or installation, expanding the agent's capabilities in response to ambiguous user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The catalog advertises deployment, infrastructure, and purchase-capable skills without embedding any cautionary metadata or user-confirmation requirements. In an agentic environment, this can increase the chance that an LLM selects an impactful skill and proceeds toward real infrastructure changes or spend-incurring operations based on ambiguous user requests.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/find_skills.py (reported line 117)May include surrounding context.

python
) -> tuple[subprocess.CompletedProcess[str], Any | None]:
    completed: subprocess.CompletedProcess[str] | None = None
    for _ in range(attempts):
        completed = subprocess.run(command, capture_output=True, text=True, check=False)
        if completed.returncode != 0:
            return completed, None
        try:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/find_skills.py (reported line 148)May include surrounding context.

python
) -> tuple[subprocess.CompletedProcess[str], Any | None]:
    completed: subprocess.CompletedProcess[str] | None = None
    for _ in range(attempts):
        completed = subprocess.run(command, capture_output=True, text=True, check=False)
        if completed.returncode != 0:
            return completed, None
        try:

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding

The code relies on npx to install skills without pinning a specific package/tool version or immutable source revision. In an installer skill, that materially increases supply-chain risk because the behavior of the fetched tooling or package can change over time or be replaced, causing execution of unexpected code during install.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

This line executes npx skills add using a package source that can come from --source or from the catalog repository value, which creates a supply-chain execution risk. Even though subprocess.run is called safely with a list and no shell, it still delegates installation and code retrieval to npx, allowing untrusted or mutable remote content to be fetched and executed in a privileged installation workflow.

Content

Scanner excerpt · scripts/find_skills.py (reported line 208)May include surrounding context.

python
return
    if shutil.which("npx") is None:
        fail("npx executable not found; install Node.js to install skills")
    completed = subprocess.run(command, capture_output=True, text=True, check=False)
    if completed.returncode != 0:
        detail = completed.stderr.strip() or completed.stdout.strip()
        fail(f"skills CLI install failed: {detail}", completed.returncode)

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
85% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · scripts/find_skills.py (reported line 239)May include surrounding context.

python
catalog: dict[str, Any], agent: str | None, scope: str, as_json: bool
) -> None:
    if shutil.which("npx") is None:
        fail("npx executable not found; install Node.js to inspect installed skills")
    installed_names = installed_skill_names(catalog, agent, scope)
    rows = [
        {

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The manifest prompt "Open a Volcengine Ark endpoint / veFaaS app / TLS project / TOS bucket." uses the verb "Open," which is ambiguous and can overlap with everyday requests such as opening, viewing, or inspecting resources rather than provisioning or enabling them. The surrounding manifest does not provide negative examples or explicit boundaries distinguishing when this plugin should activate versus when a read-only/help skill should handle the request.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.