Back to skill

Security audit

8004 Harness For Monad

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated blockchain identity purpose, but it has review-worthy security issues around wallet-key exposure and unsafe metadata fetching.

Review before installing or running. Use a minimal environment: expose AGENT_PRIVATE_KEY only to commands that must sign transactions, avoid running verify with the key present, and only verify trusted identities until tokenURI fetching is restricted to safe IPFS gateways. Expect Pinata uploads to publish metadata externally and expect the one-shot command to write an identity file locally. Prefer a locked dependency install before using this with real funds or production credentials.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/verify.mjs:5
Finding
Server-Side Request Forgery Through Untrusted On-Chain Token URI<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verify.mjs:5-8, 40-49` **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unrestricted network access **Risk Level**: High ### Vulnerable Code ```javascript function ipfsToHttp(uri, gatewayHost) { if (!uri.startsWith("ipfs://")) return uri; const cidPath = uri.replace("ipfs://", ""); return `https://${gatewayHost}/ipfs/${cidPath}`; } ``` ```javascript const gatewayHost = args.pinataGateway ?? process.env.PINATA_GATEWAY; let card = null; let registrationMatches = null; if (tokenUri && gatewayHost) { const url = ipfsToHttp(tokenUri, gatewayHost); const res = await fetch(url); if (res.ok) { card = await res.json(); const registrations = Array.isArray(card.registrations) ? card.registrations : []; const expectedRegistry = `eip155:${network.chainId}:${getAddress(network.registry)}`; registrationMatches = registrations.some( (entry) => Number(entry?.agentId) === Number(agentIdRaw) && entry?.agentRegistry === expectedRegistry ); } } ``` ### Technical Analysis The `tokenUri` value is obtained from the ERC-8004 registry for an identity selected through the user-supplied `--agentId`. An NFT owner can control that token URI. The `ipfsToHttp()` function only transforms values beginning with `ipfs://`. Every other URI is returned unchanged and subsequently passed to `fetch()`. No validation restricts the protocol, hostname, destination port, resolved IP address, or redirect destination. Consequently, an attacker-controlled identity can cause the verification process to issue HTTP requests to destinations accessible from the execution environment, including: - Loopback services such as `127.0.0.1` or `[::1]` - Private network services - Link-local addresses - Cloud instance metadata endpoints - Administrative APIs exposed only inside the host or container network The request also lacks a timeout and response-size limit. A malicious endpoint could keep th ...[truncated 1756 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject all non-IPFS token URIs if verification only needs Pinata/IPFS content: ```javascript if (!tokenUri.startsWith("ipfs://")) { throw new Error("Only ipfs:// token URIs are supported"); } ``` 2. Construct the gateway URL from a trusted, preconfigured HTTPS gateway rather than accepting an arbitrary destination derived from the token URI. 3. Validate `gatewayHost` against an explicit allowlist. Do not permit credentials, paths, ports, or schemes inside the host argument. 4. If arbitrary HTTPS metadata must be supported: - Allow only `https:`. - Resolve the hostname before connecting. - Reject loopback, private, link-local, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6. - Disable redirects or repeat destination validation after every redirect. - Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 5. Add an `AbortController` timeout and enforce a strict maximum response size before parsing JSON. 6. Validate the response content type and registration-card schema before processing it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verify.mjs:18
Finding
Read-Only Verification Unnecessarily Loads the Agent Private Key<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verify.mjs:18`; `scripts/common.mjs:74-85`; documentation at `SKILL.md:102-106, 132-133` **Vulnerability Type**: Violation of least privilege and unnecessary secret exposure **Risk Level**: Medium ### Vulnerable Code `scripts/verify.mjs` invokes the shared client factory even though it only uses the public client: ```javascript const { publicClient } = createClients(network); ``` The shared factory always retrieves and parses the private key and creates a wallet client: ```javascript export function createClients(params) { const account = privateKeyToAccount(requiredEnv("AGENT_PRIVATE_KEY")); const chain = defineChain({ id: params.chainId, name: params.networkName, nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 }, rpcUrls: { default: { http: [params.rpcUrl] } }, }); const transport = http(params.rpcUrl); const publicClient = createPublicClient({ chain, transport }); const walletClient = createWalletClient({ chain, transport, account }); return { account, publicClient, walletClient, chain }; } ``` ### Technical Analysis The verification operation only calls public view functions: - `ownerOf` - `tokenURI` - `getAgentWallet` These operations require an RPC endpoint but do not require transaction signing or access to `AGENT_PRIVATE_KEY`. Despite this, `createClients()` unconditionally reads the private key from the environment, converts it into an account object, and constructs a wallet client. This unnecessarily expands the secret's exposure boundary. All code and dependencies loaded into the verification process can potentially access the environment or account object, even though the process has no legitimate need for signing authority. The source reviewed does not explicitly transmit the raw private key. The issue is that a read-only command receives signing credentials beyond its minimum required privileges. ### Attack Path 1. A user follows the ...[truncated 1279 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Separate public and signing client construction: ```javascript export function createReadClient(params) { const chain = defineChain({ id: params.chainId, name: params.networkName, nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 }, rpcUrls: { default: { http: [params.rpcUrl] } }, }); return { chain, publicClient: createPublicClient({ chain, transport: http(params.rpcUrl), }), }; } ``` Use this read-only factory in `verify.mjs`: ```javascript const { publicClient } = createReadClient(network); ``` Retain a separate signing factory for `register.mjs`, `set-agent-uri.mjs`, and `full-register.mjs`. That signing factory alone should read `AGENT_PRIVATE_KEY`. Additionally: 1. Update `SKILL.md` to state that `AGENT_PRIVATE_KEY` is required only for transaction-writing commands. 2. Run verification in an environment where the private key variable is absent. 3. Prefer an external signer or hardware-backed signer for write operations where practical. 4. Avoid logging account objects, environment variables, or errors that could contain secret material. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/package.json:6
Finding
Security-Sensitive Dependencies Are Not Reproducibly Pinned<![CDATA[ ## Vulnerability Details **File Location**: `scripts/package.json:6-9` **Vulnerability Type**: Non-deterministic dependency resolution and supply-chain exposure **Risk Level**: Medium ### Vulnerable Code ```json "dependencies": { "pinata": "^2.5.1", "viem": "^2.38.5" } ``` No dependency lockfile was present in the reviewed project structure. ### Technical Analysis The Skill relies on two security-sensitive packages: - `viem` handles blockchain RPC communication, private-key account construction, and transaction signing. - `pinata` receives the Pinata JWT and uploads files. Caret version ranges permit package managers to resolve later compatible releases. Without a committed lockfile, the exact direct and transitive dependency versions installed in a future environment are not represented by the audited source. This does not prove that either declared package is malicious. The weakness is that the reviewed code does not provide deterministic, integrity-pinned dependency resolution. A later compromised, unexpectedly changed, or vulnerable compatible release could execute with access to: - `AGENT_PRIVATE_KEY` - `PINATA_JWT` - Agent-card file contents - Blockchain transaction parameters - Network access available to the process ### Attack Path 1. An environment installs the scripts' dependencies without a previously reviewed lockfile. 2. The package manager resolves versions allowed by the caret ranges, including transitive dependencies. 3. A newly published compatible release or transitive package contains malicious or vulnerable code. 4. The user runs a registration, upload, or verification script. 5. The dependency executes inside the same Node.js process and inherits access to environment variables, files, and network capabilities. 6. Malicious dependency code could steal credentials, alter transaction parameters, or upload different content than the user intended. This is a conditional supply-chain attack path; the audit found unsafe d ...[truncated 601 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate and commit a package-manager lockfile containing exact direct and transitive versions and integrity hashes. 2. Use deterministic installation in production, such as `npm ci`, rather than unconstrained dependency resolution. 3. Consider pinning direct dependencies to exact reviewed versions instead of caret ranges. 4. Review dependency update diffs before regenerating the lockfile. 5. Enable automated vulnerability and provenance scanning for direct and transitive packages. 6. Use a trusted package registry and enforce integrity/signature or provenance policies where available. 7. Run these scripts with the smallest possible environment: - Expose `AGENT_PRIVATE_KEY` only to signing commands. - Expose `PINATA_JWT` only to upload commands. - Restrict filesystem and network permissions where the runtime supports sandboxing. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill performs sensitive actions involving network access, environment secrets, blockchain transactions, and local file writes, but it declares no explicit tool scope or permissions boundaries. In an agent ecosystem, that omission can cause overbroad execution or poor user visibility into what resources the skill may use, increasing the chance of unintended secret exposure or unreviewed on-chain actions.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The one-shot flow automates upload of registration data to Pinata and writes identity state to local files without a prominent disclosure at the call site. That can lead users or higher-level agents to publish metadata to a third party and persist identity details locally without realizing the privacy, retention, and supply-chain implications.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code reads AGENT_PRIVATE_KEY directly from process.env and uses it to create a wallet account. This is a safety-relevant credential access operation, but the file provides no confirmation prompt, log/message, or explanatory comment/docstring warning that a private key will be consumed.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script uploads agent metadata to Pinata/IPFS as part of normal execution without any explicit user-facing warning that data will be sent to a third-party service and made content-addressable. Even if the payload seems non-secret, external transmission and durable publication can expose operational details and create irreversible data disclosure risks.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The script performs an extra side effect beyond ERC-8004 registration by creating a local identity memory file under a workspace path used by other agent workflows. This can silently influence downstream agent behavior or persist sensitive operational context in a place the user may not expect, expanding the script’s authority beyond its stated blockchain-registration purpose.

Context-Inappropriate Capability

Medium
Confidence
85% confidence
Finding
Writing into a local workspace is a privileged filesystem action that is not clearly necessary for minting or managing an ERC-8004 identity NFT. In an agent-skill context, unexplained local writes are risky because they can seed state for later tasks, create persistence, or overwrite trusted files in shared automation environments.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script defaults to writing an identity file to a fixed path (/root/.openclaw/workspace/AGENT_IDENTITY.md) without warning or confirmation. Fixed-path writes are dangerous because they can unexpectedly overwrite existing state, create unauthorized persistence, and are especially sensitive here because the path appears designed to affect agent memory or workflow behavior.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This script submits an on-chain `register` transaction immediately after parsing arguments, with no explicit user confirmation, dry-run preview, or warning about gas costs and permanent state changes. In an agent skill context, that increases the risk of unintended blockchain actions if the skill is triggered automatically or with attacker-influenced inputs, leading to unwanted identity registrations and fee expenditure.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The manifest describes a skill for registering and managing ERC-8004 Identity NFTs on Monad, which suggests blockchain identity operations. This script instead reads an arbitrary local file and uploads it to Pinata/IPFS using external credentials, a storage-publishing capability that is not mentioned in the stated purpose and is not itself ERC-8004-specific.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script reads a local file and uploads it to Pinata via a public network call, which transmits user-provided data off the system. Although the filename argument implies upload intent, the code contains no explicit confirmation prompt, warning message, or explanatory comment disclosing that the file will be publicly uploaded.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"private": true,
  "type": "module",
  "dependencies": {
    "pinata": "^2.5.1",
    "viem": "^2.38.5"
  }
}
Confidence
93% confidence
Finding
The dependency uses a caret version range, which allows automatic installation of newer minor/patch releases that were not explicitly reviewed by the skill author. In a security-sensitive blockchain skill that mints and manages on-chain identity NFTs, a compromised or breaking upstream package could alter transaction construction, key handling, or data publishing behavior and introduce supply-chain risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"type": "module",
  "dependencies": {
    "pinata": "^2.5.1",
    "viem": "^2.38.5"
  }
}
Confidence
93% confidence
Finding
The dependency uses a caret version range, so future installs may resolve to different code than was originally tested. Because this skill interacts with blockchain infrastructure via viem, an unexpected upstream change or malicious package release could affect transaction signing, RPC interactions, or chain-specific logic, making the supply-chain exposure more serious than in a non-sensitive utility.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/verify.mjs:40