Back to skill

Security audit

OpenClaw Status API

Security checks for vulnerabilities and agentic risk

Overview

This package is not clearly malicious, but it advertises a paid live status API while the reviewed code does not enforce payment and returns hard-coded health data.

Review this carefully before installing or deploying. It is suitable only as a prototype unless payment verification, recipient configuration, real health checks, dependency pinning, and external-location-data disclosure are fixed.

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
index.js:26
Finding
Payment Enforcement Is Completely Bypassed<![CDATA[ ## Vulnerability Details **File Location**: `index.js:26-31` and `server.js:27-32` **Vulnerability Type**: Missing payment authorization and verification **Risk Level**: High ### Vulnerable Code ```js // Middleware to check payment (simplified - returns 402 if no payment) function requirePayment(req, res, next) { const hasPayment = req.headers['x-payment'] || req.headers['payment-signature'] // For demo, allow requests through - in production verify payment next() } ``` The alternate server implementation contains the same issue: ```js // Check for payment header (simplified) function checkPayment(req, res, next) { const payment = req.headers['x-payment'] || req.headers['payment-signature'] // For now, allow free access - add payment verification later next() } ``` These ineffective middleware functions are attached to the paid endpoints: ```js app.get('/api/status', requirePayment, (req, res) => { ``` ```js app.get('/api/geocode', requirePayment, async (req, res) => { ``` ```js app.get('/api/weather', requirePayment, async (req, res) => { ``` ### Technical Analysis Both implementations read a possible payment header but never inspect or validate its value. They unconditionally invoke `next()`, including when neither payment header exists. Consequently, the application never calls its payment-required helper and never returns the advertised HTTP 402 response. A secure x402 implementation must validate the payment proof cryptographically and confirm the expected network, asset, amount, recipient, expiration, and replay status. Merely checking for a header would also be insufficient because an attacker could supply an arbitrary string. ### Attack Path 1. An attacker sends a request without an `x-payment` or `payment-signature` header: ```http GET /api/geocode?q=London HTTP/1.1 Host: target.example ``` 2. `requirePayment` reads two undefined header values. 3. The middleware unconditionally calls `next()`. 4. The endpoi ...[truncated 684 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject requests that do not contain a valid payment proof and return the existing HTTP 402 payment challenge. - Use a maintained x402 verification library or a trusted payment facilitator rather than implementing signature validation informally. - Cryptographically verify: - Signature authenticity and signer identity. - Base network identifier. - Exact USDC token contract. - Required amount and recipient. - Proof expiration or validity window. - Nonce or transaction uniqueness to prevent replay. - Final payment or settlement status where required by the protocol. - Call `next()` only after all verification steps succeed. - Apply the same authorization implementation consistently to every paid route. - Add per-client rate limiting, request timeouts, and monitoring to reduce abuse. - Add automated tests proving that missing, malformed, underpaid, expired, and replayed proofs are rejected. ]]>

other

Warning
Location
index.js:34
Finding
Status and Health Results Are Fabricated from Static Constants<![CDATA[ ## Vulnerability Details **File Location**: `index.js:34-55`, `server.js:35-48`, and `api/status/route.js:13-34` **Vulnerability Type**: Status data integrity failure **Risk Level**: Medium ### Vulnerable Code The deployed Express implementation returns hard-coded status records with a newly generated timestamp: ```js // Status endpoint app.get('/api/status', requirePayment, (req, res) => { res.json({ success: true, platform: 'OpenClaw Status API', timestamp: new Date().toISOString(), endpoints: ['/api/status', '/api/geocode', '/api/weather'], agents: [ { name: 'main', status: 'active' }, { name: 'ceo', status: 'active' }, { name: 'biz', status: 'active' }, { name: 'polymarket', status: 'active' }, ], cronJobs: [ { name: 'Simmer Trading', schedule: '15min', status: 'active' }, { name: 'CLAWMART Revenue', schedule: '12hr', status: 'active' }, { name: 'VIRTUALS Scanner', schedule: '1day', status: 'active' }, ] }) }) ``` The alternative route similarly constructs static data rather than retrieving status from authoritative sources: ```js async function handler(request) { // Get agent status from various sources const agents = [ { name: 'main', status: 'active', uptime: '24h' }, { name: 'ceo', status: 'active', uptime: '24h' }, { name: 'biz', status: 'active', uptime: '24h' }, { name: 'polymarket', status: 'active', uptime: '12h' }, { name: 'research', status: 'idle', uptime: '6h' }, ] const cronJobs = [ { name: 'Simmer Trading', schedule: '15min', status: 'active' }, { name: 'X402 Health', schedule: '1hr', status: 'active' }, { name: 'VIRTUALS Scanner', schedule: '1day', status: 'pending' }, { name: 'CLAWMART Revenue', schedule: '12hr', status: 'active' }, ] return NextResponse.json({ success: true, timestamp: new Date().toISOString(), agents, cronJobs, platform: 'OpenClaw', version: '1.0.0', }) ...[truncated 1536 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Retrieve agent state from authoritative runtime sources such as process supervisors, authenticated heartbeat records, or monitoring APIs. - Retrieve cron-job state from the actual scheduler and calculate health from the most recent expected and actual execution times. - Record the observation time for each individual status instead of attaching a fresh timestamp to static values. - Define freshness thresholds and return `unknown`, `stale`, or an error when authoritative data cannot be obtained. - Avoid claiming uptime unless it is measured from a reliable source. - Authenticate access to underlying monitoring systems and grant the status service read-only permissions. - Add tests that stop a monitored process or simulate a stale heartbeat and confirm that the endpoint no longer reports it as active. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:7
Finding
Malformed and Inconsistent Payment Recipient Configuration<![CDATA[ ## Vulnerability Details **File Location**: `index.js:7`, `server.js:7`, `api/status/route.js:4-10`, `README.md:30-32`, and `SKILL.md:31-33` **Vulnerability Type**: Invalid and inconsistent cryptocurrency recipient configuration **Risk Level**: Medium ### Vulnerable Code The implementation selected by `vercel.json` uses the following recipient: ```js const PAYTO = '0x483AE22AaEc52c0a1871C07E631d325bF5C8A08' ``` The documentation advertises the same value: ```md ## Wallet Payments go to: `0x483AE22AaEc52c0a1871C07E631d325bF5C8A08` ``` That value contains 39 hexadecimal digits after the `0x` prefix rather than the 40 required for an Ethereum address. Other project implementations use a different value containing an additional `3`: ```js const PAYTO = '0x483AE22AaEc52c0a1871C07E631d325b3F5C8A08' ``` ```js const routeConfig = { accepts: { scheme: 'exact', price: '$0.001', network: 'eip155:8453', payTo: '0x483AE22AaEc52c0a1871C07E631d325b3F5C8A08', }, description: 'Get OpenClaw agent status - returns health, uptime, and activity of all running agents', } ``` ### Technical Analysis Ethereum addresses require exactly 20 bytes, conventionally represented by 40 hexadecimal digits after `0x`. The recipient used by `index.js` and the documentation is malformed. Meanwhile, `server.js` and `api/status/route.js` use a distinct, correctly sized string. This disagreement creates ambiguity about the intended beneficiary. Payment clients may reject the malformed address, while deployments based on different entry points may advertise different recipients. The audit cannot establish ownership of either value; the confirmed defect is the invalid length and configuration inconsistency. ### Attack Path 1. A payment client requests a paid endpoint or follows the wallet address in the documentation. 2. The client receives or reads `0x483AE22AaEc52c0a1871C07E631d325bF5C8A08`. 3. A standards-compliant wallet or x402 client rejects the malform ...[truncated 697 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Determine and independently verify the intended Base payment recipient. - Replace every hard-coded occurrence with one valid 20-byte Ethereum address. - Validate the address at application startup with a well-reviewed Ethereum address utility. - Use an EIP-55 checksum representation and fail deployment if checksum or length validation fails. - Store the recipient in a single validated environment variable or centralized configuration source. - Ensure `index.js`, `server.js`, route configuration, `README.md`, and `SKILL.md` all derive from or document the same value. - Add configuration tests that compare the payment challenge recipient with the expected deployment recipient. ]]>

T08 · Insecure Dependencies

Note
Location
package.json:8
Finding
Dependency Resolution Is Not Reproducible<![CDATA[ ## Vulnerability Details **File Location**: `package.json:8-11` and `SKILL.md:23-29` **Vulnerability Type**: Unpinned dependency resolution without a lockfile **Risk Level**: Low ### Vulnerable Code ```json "dependencies": { "express": "^4.18.2", "axios": "^1.6.0" } ``` Installation instructions use ordinary dependency resolution: ```bash # Deploy to Vercel vercel deploy --prod # Or run locally npm install node index.js ``` The provided complete project structure contains no `package-lock.json`, `npm-shrinkwrap.json`, or other dependency lockfile. ### Technical Analysis Caret ranges permit npm to select newer compatible releases than the versions shown in `package.json`. Without a committed lockfile, direct and transitive dependency versions can vary between installations and deployment dates. No malicious package or currently exploitable dependency version was established from the reviewed source alone. The confirmed weakness is that builds are not reproducible and can silently incorporate newly released dependency or transitive-package versions without project review. ### Attack Path 1. A future direct or transitive dependency release satisfies the declared version ranges. 2. A deployment executes `npm install` without a committed lockfile. 3. npm resolves and downloads the newly available release. 4. The deployment executes the dependency as part of application installation or runtime. 5. If that release is compromised or vulnerable, the application inherits the associated supply-chain risk. This is a conditional supply-chain path; the reviewed files do not prove that a currently selected package is malicious. ### Impact Assessment The potential scope is the Node.js installation and application runtime privileges available during deployment or execution. A compromised dependency could theoretically access application environment variables, network resources, and files available to the Node process. The present confirmed impact is w ...[truncated 99 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Generate and commit a `package-lock.json` after reviewing resolved direct and transitive versions. - Use `npm ci` in continuous integration and production deployment to enforce the lockfile. - Review lockfile changes as code changes and prevent unexplained dependency updates. - Run dependency vulnerability and provenance checks in CI. - Configure automated dependency updates with testing and human approval. - Consider pinning exact direct dependency versions where strict reproducibility is required. - Rebuild and redeploy promptly after reviewed security updates rather than allowing installation-time drift. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (12)

Intent-Code Divergence

Medium
Confidence
99% confidence
Finding
The payment middleware claims to enforce payment, but it unconditionally calls next() and never validates a payment header or returns the defined 402 response. This means all endpoints protected by requirePayment are effectively free and accessible, creating an authorization and business-logic bypass for any paywalled API functionality.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The geocoding endpoint forwards the user-provided `q` search query to the external Nominatim service via `axios.get`, which transmits user input off-system. Although there is an internal comment naming the provider, there is no user-facing warning, confirmation, or disclosure in code indicating that user-supplied location queries are sent to a third party.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The weather endpoint builds a request to the external Open-Meteo API using user-supplied latitude and longitude values, which can reveal user location information. The file contains only an internal comment about the provider and no user-facing disclosure or confirmation that location data is being sent externally.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The middleware and surrounding comments imply that API access is payment-gated, but the implementation unconditionally calls next() and never verifies the payment header. This creates an authorization bypass: clients can access endpoints intended to require payment without paying, undermining the service's access-control and monetization model.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The geocode endpoint forwards the user-provided `q` parameter to the Nominatim service via `axios.get`. While the code has an internal comment noting the service is used, there is no user-facing warning, confirmation, or disclosure that user input will be transmitted to a third-party API.

External Transmission

Medium
Category
Data Exfiltration
Content
if (!lat || !lon) return res.status(400).json({ error: 'Missing lat/lon' })
  
  try {
    const response = await axios.get(`https://api.open-meteo.com/v1/forecast`, {
      params: {
        latitude: lat,
        longitude: lon,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
if (!lat || !lon) return res.status(400).json({ error: 'Missing lat/lon' })
  
  try {
    const response = await axios.get(`https://api.open-meteo.com/v1/forecast`, {
      params: {
        latitude: lat,
        longitude: lon,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The weather endpoint transmits the request's `lat` and `lon` values to `api.open-meteo.com`. Although this is part of the implementation, the file contains no user-facing warning or documentation that location data is shared with an external service.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"start": "node index.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "axios": "^1.6.0"
  }
}
Confidence
95% confidence
Finding
The dependency uses a caret range (^4.18.2), which allows npm to install newer minor and patch releases than were originally tested. This weakens build reproducibility and can unexpectedly introduce vulnerable or malicious upstream changes through the supply chain.

Unverifiable Dependency: express has 5 known advisory(ies) (CVE-2024-10491 (Express ressource injection); CVE-2014-6393 (No Charset in Content-Type Header in express); CVE-2024-9266 (Express Open Redirect vulnerability) +2 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
78% confidence
Finding
Express has known advisories, and because the manifest does not pin the version, it is not possible to verify from this file alone which exact release will be installed. The immediate issue is uncertainty rather than confirmed exploitation, but that uncertainty is a real supply-chain risk because vulnerable versions may be pulled in during install.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "express": "^4.18.2",
    "axios": "^1.6.0"
  }
}
Confidence
95% confidence
Finding
The axios dependency is specified with a caret range (^1.6.0), so installs are not fully deterministic and may resolve to different versions over time. In a supply-chain context, this increases exposure to newly introduced vulnerable releases or compromised package updates.

Unverifiable Dependency: axios has 16 known advisory(ies) (CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
80% confidence
Finding
Axios has a substantial history of security advisories, and the unpinned version means the installed release cannot be verified from the manifest alone. Given axios is commonly used for outbound HTTP requests, unresolved version ambiguity can materially increase exposure to issues such as SSRF-related bypasses or request-handling flaws if a vulnerable release is resolved.