Back to skill

Security audit

commodities

Security checks for vulnerabilities and agentic risk

Overview

This skill coherently fetches commodity prices with a local Python script, with ordinary dependency and temporary-locking risks that users should understand before running it.

Install only if you are comfortable with uv resolving yfinance at runtime and with the skill making outbound Yahoo Finance requests. In shared or high-security environments, prefer pinning dependencies and moving the lock file to a private user-owned runtime directory before use.

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 (2)

T08 · Insecure Dependencies

Warning
Location
src/main.py:3
Finding

Unpinned Third-Party Dependency Is Automatically Resolved and Executed

Content
View full analysis

Vulnerability Details

File Location: src/main.py:3-7; supporting documentation at SKILL.md:53-54
Vulnerability Type: Unpinned runtime dependency and software supply-chain exposure
Risk Level: Medium

Vulnerable Code

src/main.py:3-7:

python
# /// script
# requires-python = ">=3.12"
# dependencies = [
#   "yfinance",
# ]
# ///

SKILL.md:53-54:

markdown
## Notes
- `uv run` reads the inline `# /// script` dependency block in `main.py` and auto-installs `yfinance`.

Technical Analysis

The inline dependency declaration specifies yfinance without an exact version, lockfile, or package hash. The documented execution method uses uv run, which can resolve and install the dependency when the required environment is not already available.

Consequently, the code that executes is not fully represented by the audited project. Its effective behavior depends on whichever compatible yfinance release and transitive dependencies the package resolver selects at installation or update time. If an upstream package, maintainer account, release artifact, or transitive dependency is compromised, malicious code could execute as part of dependency installation, import, or normal library operation.

This is a supply-chain weakness rather than evidence that the currently published yfinance package is malicious.

Attack Path

  1. An attacker compromises an upstream dependency release, its publication credentials, or a transitive package used by yfinance.
  2. A user invokes the documented command in an environment where uv must resolve or install dependencies:
    bash
    uv run skills/commodities/src/main.py CL=F
    
  3. Because no audited version or artifact hash is enforced, the resolver selects the compromised compatible release.
  4. Malicious package code executes during installation, import, or use.
  5. The payload operates with the permissions and environmental ...[truncated 643 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin yfinance to a reviewed exact version rather than leaving it unconstrained:
    python
    # dependencies = [
    #   "yfinance==<reviewed-version>",
    # ]
    
  2. Generate and commit a lockfile that fixes all transitive dependency versions.
  3. Where supported, enforce cryptographic hashes for downloaded package artifacts.
  4. Configure uv to use only an approved package index over authenticated TLS.
  5. Perform dependency updates through a controlled review process that includes vulnerability scanning and release-diff inspection.
  6. Run the Skill in a restricted environment with minimal filesystem permissions, no unnecessary credentials, and limited outbound network access.

T09 · Insecure Skill Coding Practices

Note
Location
src/mutex.py:15
Finding

Predictable Shared Temporary Lock File Permits Local Denial of Service

Content
View full analysis

Vulnerability Details

File Location: src/mutex.py:15-16, 22-44
Vulnerability Type: Unsafe predictable file in a shared temporary directory
Risk Level: Low

Vulnerable Code

src/mutex.py:15-16, 22-44:

python
LOCK_FILE = os.environ.get("SKILL_MUTEX_LOCK", "/tmp/openclaw-skill.lock")
LOCK_TIMEOUT = int(os.environ.get("SKILL_MUTEX_TIMEOUT", "300"))

@contextmanager
def acquire_lock(timeout: int = LOCK_TIMEOUT):
    """Block until the global skill lock is acquired, or raise on timeout.

    Falls back to PID-based stagger (up to 30 s) if fcntl.flock is
    unavailable on the current platform or filesystem.
    """
    try:
        import fcntl

        deadline = time.monotonic() + timeout
        lock_fd = os.open(LOCK_FILE, os.O_CREAT | os.O_WRONLY, 0o644)
        try:
            while True:
                try:
                    fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
                    break
                except BlockingIOError:
                    if time.monotonic() >= deadline:
                        raise TimeoutError(
                            f"Could not acquire skill lock within {timeout}s — "
                            "another job may be stuck"
                        )
                    time.sleep(1)
            yield
        finally:
            fcntl.flock(lock_fd, fcntl.LOCK_UN)
            os.close(lock_fd)

Technical Analysis

The mutex uses the fixed path /tmp/openclaw-skill.lock, which is located in a directory normally shared by local users. The file is opened without protections such as O_NOFOLLOW, O_CLOEXEC, exclusive creation, ownership validation, or file-type validation. Its requested permissions are also 0644, although the final permissions remain subject to the process umask.

A local attacker who can create the path before the Skill first uses it may create a special file such as a FIFO. Opening a ...[truncated 1832 chars]

Remediation
View remediation

Remediation Suggestions

  1. Store the lock in a private, user-owned runtime directory rather than directly under /tmp. For example, use $XDG_RUNTIME_DIR or a directory created with mode 0700.
  2. Open the lock with defensive flags where supported:
    python
    flags = os.O_CREAT | os.O_WRONLY | os.O_CLOEXEC | os.O_NOFOLLOW
    lock_fd = os.open(lock_path, flags, 0o600)
    
  3. Use os.lstat() or os.fstat() to verify that the object is a regular file, has the expected owner, and is not group- or world-writable.
  4. Use permission mode 0600 for the lock file.
  5. Make file opening nonblocking where applicable so special files cannot block before timeout enforcement.
  6. Namespace the lock by user and Skill rather than using a single global filename.
  7. Reject unsafe paths supplied through SKILL_MUTEX_LOCK, or require configured paths to reside inside an approved private directory.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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
Findings (2)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose is a simple price lookup skill, but the associated behavior reportedly includes file locking, local lock-file use, and timeout-based process coordination. That mismatch is risky because hidden stateful or synchronization behavior can enable denial-of-service conditions, interfere with other local processes, or conceal broader functionality not justified by the user-facing description.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.