Back to skill

Security audit

AgentHire

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent testnet agent marketplace integration, but it gives an agent wallet authority to spend, release escrow, and publish task data with weak user controls.

Review this carefully before installing. Use only a fresh low-value Base Sepolia wallet, never a primary or reused private key, assume task descriptions may be visible on-chain, and do not let the agent hire or release escrow without explicit human review of the service, task text, price, result, and transaction details.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/hire.js:50
Finding
Escrow Payment Is Released Without Validating Provider Output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hire.js:50-89` **Vulnerability Type**: Automatic authorization of untrusted marketplace output **Risk Level**: High ### Vulnerable Code ```js if (status === 1 || status === 2) { // Status 1 = Submitted (need to confirm) // Status 2 = Already completed if (status === 1) { console.log("\nProvider delivered! Confirming + rating..."); try { await new Promise(r => setTimeout(r, 3000)); const cNonce = await wallet.getNonce("pending"); const confirmTx = await escrow.confirmComplete(jobId, { nonce: cNonce }); await confirmTx.wait(); console.log("Payment released!"); await new Promise(r => setTimeout(r, 3000)); const rNonce = await wallet.getNonce("pending"); const rateTx = await escrow.rateJob(jobId, 5, { nonce: rNonce }); await rateTx.wait(); console.log("Rated 5/5 stars."); } catch (e) { console.log("Auto-completed by provider."); } } else { console.log("\nJob completed!"); } // Parse result try { const r = JSON.parse(result); if (r.success) { console.log(`\nResult: Swapped ${r.amountIn} ${r.fromToken} → ${r.amountOut} ${r.toToken}`); console.log(`TX Hash: ${r.txHash}`); if (r.basescanUrl) console.log(`Verify: ${r.basescanUrl}`); console.log(`DEX: ${r.dex}`); } else { console.log(`\nJob failed: ${r.error}`); } } catch { console.log(`\nResult: ${result}`); } process.exit(0); } ``` ### Technical Analysis A provider-controlled state transition to status `1` (“Submitted”) is treated as sufficient proof that the task was completed correctly. The script calls `confirmComplete()` before parsing or validating the result. This releases the escrow payment even when the result is empty, malformed, unrel ...[truncated 1699 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse and validate the provider result before calling `confirmComplete()`. 2. Require explicit user approval before releasing escrow, especially for financial or externally verifiable tasks. 3. Define task-specific result schemas and reject missing, malformed, or contradictory fields. 4. For on-chain work, independently query the configured chain and verify the transaction hash, sender, recipient, token addresses, amounts, status, and expected state changes. 5. Use cryptographic commitments or signed provider responses where the marketplace protocol supports them. 6. Award a rating only after successful verification and user approval; never submit a fixed five-star rating. 7. Separate confirmation and rating exception handlers, preserve the actual error, and do not report a failed transaction as an automatic completion. 8. Provide a dispute, cancellation, or manual-review workflow when validation cannot establish correctness. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
setup.js:43
Finding
Wallet Private Key Is Printed and Stored Without Explicit Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `setup.js:43-71` **Vulnerability Type**: Plaintext secret exposure **Risk Level**: Medium ### Vulnerable Code ```js // 3. Generate new wallet console.log("🔑 Creating your agent wallet...\n"); const wallet = ethers.Wallet.createRandom(); console.log("═══════════════════════════════════════════"); console.log(" Your Agent Wallet"); console.log("═══════════════════════════════════════════"); console.log(" Address: " + wallet.address); console.log(" Private Key: " + wallet.privateKey); console.log("═══════════════════════════════════════════"); console.log("\n⚠️ SAVE your private key! Lost = lost forever."); console.log(" (It's also saved in .env)\n"); // 4. Write .env with hardcoded contract addresses const envContent = `# AgentHire — Agent Wallet Config # Generated: ${new Date().toISOString()} # Your agent wallet (auto-generated) AGENTHIRE_PRIVATE_KEY=${wallet.privateKey} # Base Sepolia testnet AGENTHIRE_RPC_URL=https://sepolia.base.org # Contract addresses (deployed, shared by everyone) AGENTHIRE_REGISTRY=0x506AB3D87065a60efE9C2141b891fB7099154e2E AGENTHIRE_ESCROW=0xd905035f21C0edda5971803c2aeb3eBe62312b6b `; fs.writeFileSync(envPath, envContent); console.log("📝 Saved to: " + envPath); ``` ### Technical Analysis The setup process prints the newly generated wallet private key directly to standard output. Standard output is frequently retained in terminal scrollback, CI/CD logs, remote session recordings, support transcripts, and process execution logs. Any party with access to those records can recover the wallet credential. The same key is written in plaintext to `.env` using `fs.writeFileSync()` without an explicit owner-only file mode. The resulting permissions depend on the process umask and environment. In a shared or incorrectly configured workspace, the file may be readable by other local users or processes. The private key grants complete authority over the wallet; possession i ...[truncated 1084 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never print a private key to standard output. 2. Prefer an operating-system secret store, hardware-backed wallet, encrypted keystore, or dedicated secrets manager. 3. If local file storage is unavoidable, create the file atomically with owner-only permissions, for example: ```js fs.writeFileSync(envPath, envContent, { mode: 0o600, flag: "wx" }); ``` 4. Verify the file's ownership and permissions after creation and refuse to continue if they are unsafe. 5. Add `.env` to `.gitignore` and provide only a placeholder `.env.example`. 6. Avoid placing secrets in CI output, support bundles, backups, or workspace archives. 7. Document credential rotation and wallet replacement procedures in case exposure is suspected. 8. Use a dedicated low-balance wallet and enforce the minimum permissions and funds necessary for the skill. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/hire.js:8
Finding
Sensitive Task Descriptions Can Be Permanently Disclosed On-Chain<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hire.js:8-30` **Vulnerability Type**: Public disclosure of user-supplied sensitive data **Risk Level**: Medium ### Vulnerable Code ```js async function main() { const serviceId = parseInt(process.argv[2]); const task = process.argv[3]; if (!serviceId || !task) { console.log('Usage: node hire.js <serviceId> "<task description>"'); process.exit(1); } const provider = new ethers.JsonRpcProvider(process.env.AGENTHIRE_RPC_URL); const wallet = new ethers.Wallet(process.env.AGENTHIRE_PRIVATE_KEY, provider); const registry = new ethers.Contract(process.env.AGENTHIRE_REGISTRY, registryAbi, provider); const escrow = new ethers.Contract(process.env.AGENTHIRE_ESCROW, escrowAbi, wallet); // Get service price const s = await registry.getService(serviceId); const price = s[5]; // pricePerJob in wei const name = s[2]; console.log(`Hiring ${name} (ID: ${serviceId}) for ${ethers.formatEther(price)} ETH...`); // Create job with escrow (explicit nonce for testnet) const nonce = await wallet.getNonce("pending"); const tx = await escrow.createJob(serviceId, task, { value: price, nonce }); ``` ### Technical Analysis The skill accepts an arbitrary task description as a command-line argument and passes it directly to the escrow contract's `createJob()` function. Blockchain transaction data is public and generally immutable. Consequently, any credentials, personal information, proprietary data, source code, internal URLs, or confidential instructions included in a task may become permanently available to blockchain observers. Passing the task through `process.argv` creates an additional local disclosure channel. Command-line arguments may be retained in shell history or exposed through process inspection while the command is running. There is no warning, secret detection, redaction, encryption, access control, or off-chain confidential ...[truncated 1077 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Display a prominent warning that task descriptions are public and permanent before creating a job. 2. Require explicit confirmation immediately before publishing task data. 3. Reject likely passwords, private keys, API tokens, authentication headers, and other common secret formats. 4. Do not accept sensitive task content through command-line arguments; use protected standard input or a permission-restricted input file. 5. Store task content encrypted off-chain and publish only a content hash or opaque reference. 6. Encrypt content specifically for the selected provider so other marketplace participants cannot read it. 7. Minimize submitted information and redact personal, proprietary, and authentication data by default. 8. Document that blockchain data cannot be reliably deleted and advise users never to submit secrets. ]]>

T08 · Insecure Dependencies

Note
Location
package.json:5
Finding
Dependency Installation Is Not Reproducible or Cryptographically Locked<![CDATA[ ## Vulnerability Details **File Location**: `package.json:5-7` **Vulnerability Type**: Mutable dependency resolution and supply-chain exposure **Risk Level**: Low ### Vulnerable Code ```json "dependencies": { "ethers": "^6.13.0", "dotenv": "^16.4.0" } ``` The setup process installs the mutable dependency set in `setup.js:12-16`: ```js if (!fs.existsSync(path.join(dir, "node_modules"))) { console.log("📦 Installing dependencies..."); try { execSync("npm install --production", { cwd: dir, stdio: "inherit" }); console.log("✅ Dependencies installed.\n"); } catch { console.error("❌ npm install failed. Run manually: cd " + dir + " && npm install"); process.exit(1); } } ``` ### Technical Analysis The dependency declarations use caret ranges, allowing npm to resolve later compatible releases. No package lockfile was present in the audited project structure. As a result, separate installations can retrieve different package versions even when the skill source has not changed. The setup script invokes `npm install --production`, which resolves packages from the configured npm registry and may execute dependency lifecycle scripts. The effective installed code is therefore not fixed to the code reviewed during this audit. No evidence was found that the currently named packages are malicious. The risk is the absence of deterministic dependency controls, which increases exposure to a future compromised release, registry compromise, or unsafe transitive dependency update. ### Attack Path 1. A permitted direct or transitive dependency version is published after the skill was reviewed. 2. That version contains a compromised lifecycle script or malicious runtime behavior. 3. A user runs `setup.js` without an existing `node_modules` directory. 4. `npm install --production` resolves and downloads the newer dependency graph. 5. Malicious lifecycle code may execute with the privileges of the installing user, or ...[truncated 677 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin direct dependencies to exact reviewed versions rather than caret ranges. 2. Generate, review, and commit `package-lock.json`. 3. Replace `npm install --production` with `npm ci --omit=dev` so installation fails if the manifest and lockfile disagree. 4. Review dependency and lockfile changes before accepting automated updates. 5. Use `npm audit`, software composition analysis, and registry integrity monitoring. 6. Consider `--ignore-scripts` when dependency lifecycle scripts are not required. 7. Use a trusted registry configuration and protect project-level npm configuration from unauthorized overrides. 8. Periodically update pinned versions through a controlled review and testing process rather than resolving new versions during setup. ]]>
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 (22)

Credential Access

High
Category
Privilege Escalation
Content
cd ~/.openclaw/skills/agenthire && npm install

# Configure
cp .env.example .env
# Edit .env with your contract addresses and wallet key
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# Configure
cp .env.example .env
# Edit .env with your contract addresses and wallet key
```

After setup, run `openclaw skills list` — you should see:
Confidence
77% confidence
Finding
The README explicitly instructs the user to place a wallet private key into a .env file for a skill that performs autonomous on-chain hiring and swaps. While common in prototypes, encouraging raw private key storage for an agent-executed financial workflow increases the chance of credential compromise, accidental leakage, or misuse by the agent runtime or surrounding tooling.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill description promises hiring, payment, and escrow behavior, but the analyzed material does not substantiate those capabilities and appears to describe more functionality than is actually present. This mismatch can mislead operators into authorizing financial or delegated actions under false assumptions, weakening trust and safe review of blockchain-affecting behavior.

Ae1

High
Category
analysis-evasion
Content
cd ~/.openclaw/workspace/skills/agenthire && node scripts/search.js "token-swap"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
cd ~/.openclaw/workspace/skills/agenthire && node scripts/search.js "token-swap"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

High
Confidence
98% confidence
Finding
The script creates an on-chain escrow transaction that transfers value immediately based only on CLI arguments and contract-returned pricing, with no interactive confirmation, simulation, or explicit irreversible-funds warning. In an agent-to-agent marketplace context, this is especially dangerous because another agent or automation layer could invoke the script and spend funds without meaningful human review.

Missing User Warnings

High
Confidence
99% confidence
Finding
The script automatically calls confirmComplete when a provider marks a job as submitted, which releases escrowed payment without validating that the result is correct or obtaining user approval. In this marketplace setting, an untrusted or low-quality provider can submit bogus output and still get paid because the client script finalizes payment programmatically after a short delay.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env node
// agenthire_status — Check job status
require("dotenv").config({ path: __dirname + "/../.env" });
const { ethers } = require("ethers");
const escrowAbi = require("./JobEscrow.abi.json");
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env node
// agenthire_status — Check job status
require("dotenv").config({ path: __dirname + "/../.env" });
const { ethers } = require("ethers");
const escrowAbi = require("./JobEscrow.abi.json");
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env node
// agenthire_status — Check job status
require("dotenv").config({ path: __dirname + "/../.env" });
const { ethers } = require("ethers");
const escrowAbi = require("./JobEscrow.abi.json");
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env node
// agenthire_status — Check job status
require("dotenv").config({ path: __dirname + "/../.env" });
const { ethers } = require("ethers");
const escrowAbi = require("./JobEscrow.abi.json");
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
console.log("\n⚠️  SAVE your private key! Lost = lost forever.");
console.log("   (It's also saved in .env)\n");

// 4. Write .env with hardcoded contract addresses
const envContent = `# AgentHire — Agent Wallet Config
# Generated: ${new Date().toISOString()}
Confidence
96% confidence
Finding
This code path explicitly informs the user that the wallet private key will be saved in `.env`, and the surrounding logic does in fact persist the secret in plaintext. In the context of an agent marketplace that instructs users to fund the wallet, storing the credential in an unprotected environment file materially increases the risk of credential theft and financial loss.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README instructs users to configure a private key and describes triggering real on-chain swaps through the agent, but it does not include clear warnings about irreversible financial actions, key handling, or limiting wallet exposure. In an agent-driven marketplace context, this is especially risky because automated tool use can cause unintended transactions or expose funds if the configured wallet is overprivileged or compromised.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill declares sensitive environment requirements, including a private key, but does not define an explicit tool scope or permissions boundary. That creates ambiguity about what the skill is allowed to access and increases the risk of over-broad execution or accidental exposure/use of secrets by the runtime or downstream scripts.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs the agent to send tasks to third-party agents and pay them on-chain, but it does not require a clear user warning or consent before sharing task details externally. This is dangerous because user prompts may contain sensitive financial, strategic, or personal information that would be disclosed to an untrusted provider and coupled with an irreversible escrow payment.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**Returns:** Job result from the hired agent. Includes TX hash verifiable on BaseScan.

**Note:** This command waits up to 90 seconds for the provider to complete the job. It auto-confirms and rates 5/5 on success.

### agenthire_status
Confidence
91% confidence
Finding
The auto-confirm behavior represents autonomous decision-making over a financially meaningful action: releasing escrow after a third party claims success. In this context, autonomy is especially dangerous because blockchain actions are difficult or impossible to reverse, and the skill is making trust and payment decisions without human verification.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill states that jobs are automatically confirmed and rated 5/5 on success without a clear warning or user approval. Auto-confirmation can irreversibly release escrowed funds and submit a reputation signal before the user has verified the output, enabling loss of funds, poor-quality service acceptance, or abuse by malicious providers.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The code constructs a wallet directly from `AGENTHIRE_PRIVATE_KEY`, which is a sensitive credential access operation. While the script has some operational logging, it does not disclose that it will read and use a private key from the environment, and there is no comment or prompt warning the user about this sensitive action.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The setup script automatically executes `npm install --production` via `execSync`, which runs lifecycle scripts from this package and all dependencies during installation. That creates an unnecessary code-execution path in a post-install context, increasing supply-chain risk and giving the package the ability to trigger networked or local side effects beyond simple marketplace configuration.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The script generates a blockchain private key and writes it directly into a plaintext `.env` file without obtaining explicit consent immediately before the write or validating secure storage expectations. A wallet private key is a highly sensitive credential; if the repository, host, backups, logs, or adjacent tooling expose `.env`, an attacker can take over the wallet and spend any funds placed into it.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"version": "0.1.0",
  "private": true,
  "dependencies": {
    "ethers": "^6.13.0",
    "dotenv": "^16.4.0"
  }
}
Confidence
96% confidence
Finding
The dependency on ethers uses a caret range (^6.13.0), which permits automatic installation of newer compatible releases rather than a single audited version. This increases supply-chain risk because a compromised or breaking upstream release could be pulled into builds unexpectedly, which is especially relevant for a skill that may handle on-chain interactions and payments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"private": true,
  "dependencies": {
    "ethers": "^6.13.0",
    "dotenv": "^16.4.0"
  }
}
Confidence
95% confidence
Finding
The dotenv dependency is also specified with a caret range (^16.4.0), allowing silent drift to later patch/minor releases. While dotenv is not directly high-risk by itself, any unpinned third-party package introduces supply-chain uncertainty and could affect secret handling or runtime behavior if a malicious or flawed version is published.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
setup.js:16