Back to skill

Security audit

Agent Well-Known Readiness Audit

Security checks for vulnerabilities and agentic risk

Overview

The skill’s audit workflow is mostly disclosed, but its installer can overwrite or delete directories outside the intended OpenClaw skills folder if given a crafted install name.

Review before installing. Do not run scripts/install_skill.py with an untrusted --name value, and avoid --force until the installer validates that the destination stays inside the selected skills directory. If you use the skill, keep payment approval in your wallet policy and verify the x402 price, network, and payTo before any paid call.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/install_skill.py:16
Finding
Unvalidated installation name permits directory escape and recursive deletion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_skill.py`, lines 16-21 and 54-65 **Vulnerability Type**: Path traversal leading to arbitrary filesystem deletion and replacement **Risk Level**: High ### Vulnerable Code ```python def copytree(src: pathlib.Path, dst: pathlib.Path, force: bool): if dst.exists(): if not force: raise SystemExit(f"target exists: {dst} (pass --force to overwrite)") shutil.rmtree(dst) ignore = shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store") shutil.copytree(src, dst, ignore=ignore) ``` ```python parser.add_argument("--target", default=str(DEFAULT_TARGET), help="Skills directory, default: OPENCLAW_SKILLS_DIR or ~/.openclaw/skills") parser.add_argument("--name", default=PACKAGE_DIR.name, help="Installed directory name") parser.add_argument("--force", action="store_true", help="Overwrite an existing installed copy") parser.add_argument("--skip-package-verify", action="store_true", help="Skip bundled checksum verification before install") parser.add_argument("--verify-backend", action="store_true", help="Run scripts/verify_backend.py after install when present") args = parser.parse_args() if not args.skip_package_verify: code = run_package_verify(PACKAGE_DIR) if code != 0: return code target = pathlib.Path(args.target).expanduser().resolve() dest = target / args.name target.mkdir(parents=True, exist_ok=True) copytree(PACKAGE_DIR, dest, args.force) ``` ### Technical Analysis The installer treats `--name` as a trusted directory name but does not verify that it is a single safe path component. `pathlib` permits this argument to contain parent-directory components such as `../` or to be an absolute path. If `args.name` is absolute, the expression `target / args.name` resolves to the absolute value and discards the intended target prefix. If it contains `../`, filesystem operations normalize those components when accessing the path, allowing the dest ...[truncated 1794 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `--name` to be exactly one directory component: - Reject absolute paths. - Reject `.` and `..`. - Reject `/`, `\`, and platform-specific path separators. - Reject names for which `pathlib.Path(name).name != name`. 2. Resolve the final destination and enforce containment before any write or deletion: ```python target = pathlib.Path(args.target).expanduser().resolve() name = pathlib.Path(args.name) if name.is_absolute() or name.name != args.name or args.name in {".", ".."}: raise SystemExit("--name must be a single safe directory name") dest = (target / name).resolve() if dest.parent != target: raise SystemExit("installation destination escapes the selected target") ``` 3. Before calling `shutil.rmtree()`, repeat the containment check and explicitly refuse dangerous destinations such as the filesystem root, home directory, target root, or package source directory. 4. Consider replacing destructive overwrite behavior with an atomic backup-and-rename process. Require explicit confirmation when deleting a nonempty destination. 5. Add regression tests covering absolute names, `../` traversal, nested names, symlink-related edge cases, empty names, and platform-specific separators. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/verify_package.py:30
Finding
Package integrity verification accepts unlisted files that are later installed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verify_package.py`, lines 30-44 **Related Location**: `scripts/install_skill.py`, lines 16-22 and 63-65 **Vulnerability Type**: Incomplete package manifest verification **Risk Level**: Medium ### Vulnerable Code ```python checksums = load_checksums() failures = [] verified = [] for rec in checksums.get("files", []): rel = rec["path"] path = PACKAGE_DIR / rel if not path.exists(): failures.append({"path": rel, "error": "missing"}) continue actual = sha256_path(path) if actual != rec.get("sha256"): failures.append({"path": rel, "error": "sha256_mismatch", "expected": rec.get("sha256"), "actual": actual}) else: verified.append(rel) result = {"ok": not failures, "slug": checksums.get("slug"), "version": checksums.get("version"), "verified_count": len(verified), "failures": failures} ``` The installer subsequently copies the complete package tree: ```python ignore = shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store") shutil.copytree(src, dst, ignore=ignore) ``` ### Technical Analysis The verifier iterates only over entries declared in `checksums.json`. It checks that those listed files exist and match their recorded SHA-256 values, but it never enumerates the package directory to detect unexpected files. Consequently, an attacker can append an unlisted script, nested Skill content, configuration file, or other payload without changing any listed file. Checksum verification reports success because all known records still match. The installer then uses `shutil.copytree()` to copy the complete directory, including the unlisted payload. The verifier also accepts an absent or empty `files` array because `checksums.get("files", [])` produces no failures. In addition, the checksum manifest is bundled with the same package and is not authenticated by an externally pinned digest or signature. These properties make the integrity check weaker ...[truncated 1385 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Recursively enumerate all package files and compare the resulting set with the paths declared in `checksums.json`. 2. Fail verification when: - A declared file is missing. - A declared digest does not match. - An undeclared file or directory is present. - The manifest has no valid file records. - A manifest path is absolute, contains `..`, escapes the package root, or points to a non-regular file. 3. Define a small explicit exclusion list for generated files such as `__pycache__`, bytecode, and the installed lock file. Do not silently accept arbitrary extras. 4. Verify file type and resolved-path containment before hashing to prevent symlink or path-manipulation issues. 5. Authenticate `checksums.json` using a signature or digest distributed through an independent trusted channel. A manifest stored only beside the files it validates does not protect against an attacker capable of replacing both. 6. Make the installer copy only the verified manifest entries into a newly created destination rather than copying the entire source tree. 7. Add tests proving that verification fails for extra files, empty manifests, duplicate paths, traversal paths, symlinks, and modified manifests. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill advertises broad auditing of arbitrary agent/API origins, OpenAPI/MCP readiness, and install blockers, but the visible content hard-codes a single backend and directs the agent to rely on that hosted service for readiness decisions. This can mislead operators about what evidence is being gathered and may cause trust, payment, or integration decisions to be based on a narrower or opaque backend assessment than the description suggests.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill advertises broad auditing of arbitrary agent/API origins, OpenAPI/MCP readiness, and install blockers, but the visible content hard-codes a single backend and directs the agent to rely on that hosted service for readiness decisions. This can mislead operators about what evidence is being gathered and may cause trust, payment, or integration decisions to be based on a narrower or opaque backend assessment than the description suggests.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill advertises broad auditing of arbitrary agent/API origins, OpenAPI/MCP readiness, and install blockers, but the visible content hard-codes a single backend and directs the agent to rely on that hosted service for readiness decisions. This can mislead operators about what evidence is being gathered and may cause trust, payment, or integration decisions to be based on a narrower or opaque backend assessment than the description suggests.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill advertises broad auditing of arbitrary agent/API origins, OpenAPI/MCP readiness, and install blockers, but the visible content hard-codes a single backend and directs the agent to rely on that hosted service for readiness decisions. This can mislead operators about what evidence is being gathered and may cause trust, payment, or integration decisions to be based on a narrower or opaque backend assessment than the description suggests.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The manifest describes an auditing skill that evaluates external agent/API origins before integration. This file deletes existing directories, copies the package into a skills directory, creates target directories, and writes a lock file, which are package installation behaviors not justified by the stated auditing purpose.

Self-Modification

High
Category
Rogue Agent
Content
"version": checksums.get("version", "1.0.0"),
        "installed_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        "checksums_file": "checksums.json",
        "pinning": "Do not auto-update this skill. Reinstall only after reviewing a new ClawMart version and re-running scripts/verify_package.py.",
    }
    (dest / ".clawmart-lock.json").write_text(json.dumps(lock, indent=2) + "\n")
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The skill is described as auditing an origin for readiness and install blockers, implying analysis of a target service or API. The implemented behavior instead installs the skill into a local OpenClaw directory and optionally runs a backend verifier after installation, which is semantically different from auditing an origin before integration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The installer executes a bundled verifier program from the package being installed, which means untrusted package content gets code execution during installation. For an audit-oriented skill, this expands capability beyond passive analysis and creates a supply-chain trust problem if the package or verifier is modified.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
verifier = package_dir / "scripts" / "verify_package.py"
    if not verifier.exists():
        raise SystemExit("missing scripts/verify_package.py; refusing install without package integrity check")
    return subprocess.call([sys.executable, str(verifier)])


def write_lock(dest: pathlib.Path):
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
verifier = package_dir / "scripts" / "verify_package.py"
    if not verifier.exists():
        raise SystemExit("missing scripts/verify_package.py; refusing install without package integrity check")
    return subprocess.call([sys.executable, str(verifier)])


def write_lock(dest: pathlib.Path):
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

External Transmission

Medium
Category
Data Exfiltration
Content
def main():
    parser = argparse.ArgumentParser(description="Print safe curl examples for this ClawMart package.")
    parser.add_argument("--name", help="Only print one example by name.")
    parser.add_argument("--paid", action="store_true", help="Also show where to place a payment header. Do not paste private keys here.")
    args = parser.parse_args()
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Vague Triggers

Low
Confidence
80% confidence
Finding
This JSON manifest-style file enumerates package metadata and bundled files but provides no trigger phrases, activation conditions, or exclusion criteria. For manifest files, missing specificity on when the skill should activate can lead downstream systems to infer overly broad invocation behavior.

Intent-Code Divergence

Low
Confidence
96% confidence
Finding
The argument parser description says this script prints safe curl examples for a 'ClawMart package', which contradicts the surrounding skill context of auditing agent/API origins for well-known discovery, x402 pricing, OpenAPI/MCP readiness, and install blockers. This appears to be copied documentation from another project and misstates the script's intent.

Static analysis

No suspicious patterns detected.