Back to skill

Security audit

verity-guard

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent and mostly transparent, but it asks users to expose a spend-capable wallet key to unpinned external code and recommends an unpinned privileged plugin.

Install only if you are comfortable sending checked content to VerityLayer and using a dedicated low-balance Base wallet. Pin and review the `verity-guard[x402]` package before exposing `VERITY_WALLET_KEY`, and do not install the optional OpenClaw plugin unless you can pin or verify the exact plugin version and understand its hook permissions.

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

T08 · Insecure Dependencies

Error
Location
scripts/verity.py:60
Finding
Unpinned Third-Party SDK Receives a Spend-Capable Wallet Private Key<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:59-63`; `scripts/verity.py:60-67` **Vulnerability Type**: Unpinned security-sensitive dependency **Risk Level**: High ### Vulnerable Code `SKILL.md:59-63`: ```markdown One-time install of the client (the script tells you if it is missing): ```bash pip install "verity-guard[x402]" ``` ``` `scripts/verity.py:60-67`: ```python try: from verity_guard import x402_payer return VerityClient(http=x402_payer(key)) except ImportError: _die(3, INSTALL_HINT) except Exception as e: # bad key, etc. — never echo the key itself _die(1, f"could not build the x402 payer: {type(e).__name__}: {str(e)[:160]}") ``` ### Technical Analysis The documented installation command installs `verity-guard[x402]` without an exact version, lockfile, package hash, or other integrity constraint. The resulting third-party code is then imported into the Python process and passed the raw value of `VERITY_WALLET_KEY`. The local wrapper does not explicitly transmit or log the private key. However, its security guarantee depends on external package code that is not included in this project and was therefore outside the audit scope. Because the dependency is unpinned, a later package release, compromised distribution account, or compromised dependency in the package's transitive dependency graph could change the behavior after this skill has been reviewed. Passing the raw key to `x402_payer(key)` grants that dependency access to the complete private key rather than only to a narrowly scoped signature. A malicious version could copy the key, inspect other process environment variables, or generate signatures beyond the intended payment authorization. ### Attack Path 1. An attacker compromises the upstream package publishing account, package repository, release process, or a relevant transitive dependency. 2. The attacker publishes a malicious version that retains the expected `verity_ ...[truncated 1169 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the client and all security-sensitive transitive dependencies to audited exact versions. 2. Publish a lockfile with cryptographic hashes and install using hash verification, such as: ```bash pip install --require-hashes -r requirements.txt ``` 3. Verify upstream package provenance and signed release artifacts before updating the approved version. 4. Review every dependency update before changing the pin, especially code involved in wallet handling, HTTP transport, and x402 payment authorization. 5. Prefer an isolated signing component that does not disclose the raw private key to the SDK. The SDK should receive only a narrowly scoped authorization or signing interface that enforces the chain, token, recipient, amount, expiration, and nonce. 6. Run the client in a restricted environment with minimal filesystem, environment-variable, and network access. 7. Continue requiring a dedicated low-balance wallet and document explicit balance and allowance limits as defense-in-depth measures. ]]>

T08 · Insecure Dependencies

Error
Location
references/enforcement.md:64
Finding
Unpinned External Enforcement Plugin Is Recommended for Privileged Tool Interception<![CDATA[ ## Vulnerability Details **File Location**: `references/enforcement.md:64-72` **Vulnerability Type**: Unpinned privileged plugin dependency **Risk Level**: High ### Vulnerable Code ```markdown ## Status: the plugin exists — use it if you need a gate **`@veritylayer/openclaw-plugin`** implements tier 1 above: a `before_tool_call` hook that verifies every agent-initiated `exec` / `write` / `edit` / `apply_patch` / `process` / `code_execution` / `terminal` call and maps the verdict onto OpenClaw's own controls. ```bash openclaw plugins install @veritylayer/openclaw-plugin ``` ``` ### Technical Analysis The project recommends installing `@veritylayer/openclaw-plugin` without specifying an audited version or integrity digest. The plugin implementation is not included in this project, so its behavior could not be independently reviewed during this audit. According to the documentation, the plugin registers a `before_tool_call` hook for highly privileged operations, including command execution and file modification. A plugin operating at this boundary can observe tool parameters and influence whether or how sensitive operations execute. Installing a mutable, unpinned package at this trust boundary creates a significant supply-chain risk. A compromised future release could inspect sensitive commands and file contents, manipulate tool parameters, weaken approval behavior, or execute logic under the permissions available to the OpenClaw plugin environment. ### Attack Path 1. An attacker compromises the plugin's package publishing account, distribution infrastructure, release process, or a transitive dependency. 2. The attacker publishes a malicious release under the existing package name. 3. An operator follows the unversioned installation instruction: `openclaw plugins install @veritylayer/openclaw-plugin`. 4. The package manager resolves and installs the attacker-controlled release. 5. The plugin loads into OpenClaw and registers privileged tool ...[truncated 966 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the unversioned command with an exact, audited plugin version. 2. Pin and verify the package using a supported integrity digest, signed artifact, or immutable package reference. 3. Publish the plugin source and reproducible-build instructions so operators can compare the installed artifact with the reviewed source. 4. Audit the plugin and its transitive dependencies before recommending a release. 5. Require explicit review for every version update rather than automatically resolving the latest release. 6. Run the plugin with the minimum permissions necessary and restrict its filesystem, environment, and outbound network access where supported. 7. Document the plugin's exact hook behavior, data sent externally, failure modes, and parameter-rewrite capabilities. 8. Maintain a trusted-version allowlist so an unexpected package update cannot silently enter the tool-call enforcement path. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (4)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Before anything irreversible** — use `check` (allow / review / block):
- moving money, signing a transaction, approving a spend
- sending something a human will see: email, DM, post, reply
- destructive or state-changing commands: `rm -rf`, `DROP`, `git push --force`, deploy, terminate
- credential / permission / access-control changes, adding forwarding rules or webhooks
- publishing, or making something public
Confidence
70% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill explicitly relies on environment access via `VERITY_WALLET_KEY` but does not declare any tool scope or permission boundary for that capability. This creates an under-specified trust boundary: a host agent may expose sensitive environment data to the skill without clear operator consent or sandboxing expectations, increasing the risk of secret misuse or accidental overexposure.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The catalog instructs users to directly POST arbitrary inputs such as claims, content, policies, payloads, and evidence to external endpoints, but it does not clearly warn that this data will leave the local system and be transmitted to third-party services. In a security-oriented skill, this omission is especially risky because the very data being checked may contain sensitive prompts, internal context, PII, secrets, or confidential business information.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
elif a.policy:
        _die(1, "--policy applies to `check` only")

    res = getattr(_client(paid=True), method)(a.text, **kwargs)

    # Fail-closed reporting, driven off the SDK's chokepoint rather than a hand-rolled list.
    # Hand-rolling it here missed a whole class: a 200 that parses but carries no decision
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.