Back to skill

Security audit

OpenClaw Leaderboard

Security checks for vulnerabilities and agentic risk

Overview

The leaderboard skill matches its broad purpose, but it needs review because it encourages public sharing of sensitive agent prompts/proof and has avoidable credential-handling risks.

Install or use this only if you are comfortable sending leaderboard data to the service and making submitted proof/configuration public. Do not submit raw system prompts, secrets, customer data, private URLs, account details, or unredacted financial screenshots. Store any API key in a proper secret manager, not agent memory or a loose JSON file, and avoid using the bundled tool with a custom leaderboard URL unless you fully control and trust that endpoint.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (6)

T09 · Insecure Skill Coding Practices

Error
Location
openclaw-skill/tool.js:4
Finding
Bearer Credentials and Sensitive Submission Data Can Be Redirected to an Arbitrary Host<![CDATA[ ## Vulnerability Details **File Location**: `openclaw-skill/tool.js:4-15, 127-134, 187-195` **Vulnerability Type**: Unvalidated destination for authenticated network requests **Risk Level**: High ### Vulnerable Code ```js const BASE_URL = process.env.OPENCLAW_LEADERBOARD_URL || "https://openclaw-leaderboard.vercel.app"; function getApiKey() { return process.env.OPENCLAW_API_KEY || null; } function authHeaders() { const key = getApiKey(); if (!key) return {}; return { Authorization: `Bearer ${key}` }; } ``` ```js const res = await fetch(`${BASE_URL}/api/v1/submissions`, { method: "POST", headers: { "Content-Type": "application/json", ...authHeaders(), }, body: JSON.stringify(body), }); ``` ```js async function myProfile() { const key = getApiKey(); if (!key) throw new Error("OPENCLAW_API_KEY not set. Register first."); const res = await fetch(`${BASE_URL}/api/v1/agents/me`, { headers: { Authorization: `Bearer ${key}` }, }); ``` ### Technical Analysis The base URL for every API request can be overridden through `OPENCLAW_LEADERBOARD_URL`. The code does not validate the URL scheme, hostname, port, or redirect destination before attaching the reusable bearer credential. This behavior contradicts the Skill documentation's claim that the API key is only sent to the official leaderboard domain. An attacker who can influence the process environment can direct authenticated requests to an attacker-controlled endpoint. The submission request may additionally contain earnings details, proof references, system prompts, tool inventories, model configuration, and free-form configuration notes. This override is not required for the Skill's ordinary public-leaderboard functionality and exceeds the minimum safe network privilege unless the destination is strictly constrained. ### Attack Path 1. The victim configures `OPENCLAW_API_KEY` for normal authenticated use. 2. An attacker-controlled launcher, environment file, ...[truncated 852 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `OPENCLAW_LEADERBOARD_URL` from production Skill execution and use a fixed HTTPS origin. - If custom endpoints are necessary for development, parse the URL with `new URL()` and require: - `https:` as the scheme. - An exact allowlisted hostname. - An approved port. - No embedded username or password. - Add authorization headers only after validating the final destination. - Disable automatic cross-origin redirects for authenticated requests, or validate every redirect target before forwarding credentials. - Separate public unauthenticated reads from authenticated requests so credentials are only attached where required. - Add tests proving that credentials cannot be sent to non-allowlisted hosts. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/app/api/v1/submissions/[id]/route.ts:19
Finding
Unauthenticated Endpoint Publicly Exposes Complete Agent Prompts and Configuration<![CDATA[ ## Vulnerability Details **File Location**: `src/app/api/v1/submissions/[id]/route.ts:19-64` **Vulnerability Type**: Public disclosure of sensitive agent configuration **Risk Level**: High ### Vulnerable Code ```ts const submission = await prisma.submission.findUnique({ where: { id }, include: { votes: { select: { voteType: true }, }, }, }); if (!submission) { return NextResponse.json( { error: "Submission not found" }, { status: 404 } ); } const data = { id: submission.id, openclawInstanceId: submission.openclawInstanceId, openclawName: submission.openclawName, description: submission.description, amountCents: submission.amountCents, currency: submission.currency, proofType: submission.proofType, proofUrl: submission.proofUrl, proofDescription: submission.proofDescription, transactionHash: submission.transactionHash, verificationMethod: submission.verificationMethod, status: submission.status, systemPrompt: submission.systemPrompt, modelId: submission.modelId, modelProvider: submission.modelProvider, tools: submission.tools, modelConfig: submission.modelConfig, configNotes: submission.configNotes, createdAt: submission.createdAt.toISOString(), updatedAt: submission.updatedAt.toISOString(), legitVotes: submission.votes.filter((v) => v.voteType === "LEGIT").length, suspiciousVotes: submission.votes.filter( (v) => v.voteType === "SUSPICIOUS" ).length, }; return NextResponse.json({ data }); ``` The corresponding storage path accepts these values without field-level confidentiality controls: ```ts const { tools, modelConfig, ...rest } = parsed.data; const submission = await prisma.submission.create({ data: { ...rest, submitterIpHash: ipHash, ...(agentId && { agentId }), ...(tools && { tools: tools as unknown as import("@prisma/client").Prisma.InputJsonValue }), ...(modelConfig && { modelConfig: modelConfig as unknown as import("@prisma/client").P ...[truncated 1778 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Define an explicit allowlist of fields safe for unauthenticated responses. - Keep `systemPrompt`, `tools`, `modelConfig`, `configNotes`, and sensitive proof material private by default. - Require explicit, informed, field-level consent before publication. - Provide separate public and owner-only API response models. - Apply authentication and ownership checks to private submission details. - Scan prompt, configuration, and notes fields for credentials, tokens, private keys, and sensitive URLs before storage or publication. - Redact detected secrets and reject submissions that contain high-confidence credentials. - Allow submitters to edit, unpublish, and delete sensitive configuration. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/app/api/v1/agents/register/route.ts:54
Finding
Reusable API Keys Are Persisted and Queried in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `src/app/api/v1/agents/register/route.ts:54-75`; `src/lib/auth.ts:20-31` **Vulnerability Type**: Plaintext storage of bearer credentials **Risk Level**: High ### Vulnerable Code ```ts const apiKey = generateApiKey(); const claimToken = generateClaimToken(); const claimUrl = `${SITE_URL}/claim/${claimToken}`; const agent = await prisma.agent.create({ data: { name: parsed.data.name, description: parsed.data.description ?? null, apiKey, claimUrl, }, }); return NextResponse.json( { agent: { name: agent.name, api_key: apiKey, claim_url: claimUrl, }, important: "Save your api_key! You need it for all authenticated requests.", }, { status: 201 } ); ``` ```ts const apiKey = authHeader.slice(7).trim(); if (!apiKey) { return { error: NextResponse.json( { error: "Invalid API key format" }, { status: 401 } ), agent: null, }; } const agent = await prisma.agent.findUnique({ where: { apiKey } }); if (!agent) { return { error: NextResponse.json( { error: "Invalid API key" }, { status: 401 } ), agent: null, }; } ``` ### Technical Analysis Registration stores the raw API key in the agent record, and authentication performs a direct database lookup using the supplied plaintext value. This confirms that the reusable bearer credential is retained in recoverable form rather than as a one-way verifier. Bearer credentials grant access based solely on possession. Consequently, database read access, an administrative export, an exposed backup, or an overly broad internal query would reveal immediately usable credentials. ### Attack Path 1. An agent registers and receives an `ocl_...` API key. 2. The application stores the same raw key in the database. 3. An attacker gains read access to the database, a backup, a log/export containing records, or a compromised database administration account. 4. The attacker ...[truncated 501 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Generate each API key as a public lookup identifier plus a high-entropy secret. - Store only a keyed cryptographic hash or password-style verifier of the secret. - Retain a short non-secret prefix for indexed lookup and user identification. - Verify presented keys with a constant-time comparison. - Display the raw API key only once during registration. - Implement key expiration, rotation, revocation, and last-used auditing. - Avoid logging authorization headers or raw API keys. - Rotate all existing keys after migrating the storage model because previously stored plaintext values must be considered exposed if database access was not tightly controlled. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
openclaw-skill/SKILL.md:59
Finding
Skill Documentation Recommends Persistent Plaintext Credential Storage<![CDATA[ ## Vulnerability Details **File Location**: `openclaw-skill/SKILL.md:59-68` **Vulnerability Type**: Unsafe local secret persistence guidance **Risk Level**: Medium ### Vulnerable Code ```md **⚠️ Save your `api_key` immediately!** You need it for all authenticated requests. **Recommended:** Save your credentials to memory or `~/.config/openclaw/credentials.json`: ```json { "api_key": "ocl_xxx", "agent_name": "YourAgentName" } ``` ``` ### Technical Analysis The Skill explicitly recommends retaining a reusable bearer credential in agent memory or in a predictable JSON file. It does not require restrictive filesystem permissions, encryption, an operating-system credential store, or exclusion from backups and version control. Saving the key in agent memory can also cause it to be surfaced in later prompts, memory exports, debugging output, or unrelated sessions. The predictable path makes credential discovery easier for other local processes or tools operating with the same user privileges. ### Attack Path 1. A user follows the Skill's recommended registration procedure. 2. The user or agent saves the API key in persistent memory or `~/.config/openclaw/credentials.json`. 3. The file is created with permissive default permissions, included in a backup, copied into a support bundle, or read by another same-user tool. 4. Alternatively, a later prompt causes the agent to retrieve and reveal the remembered credential. 5. The attacker obtains the key and uses it as a bearer token to impersonate the agent. ### Impact Assessment A local process, another agent, a memory consumer, or a user with access to backups may obtain the API key. The attacker receives the same API privileges as the affected agent until the key is revoked. This does not independently grant operating-system privilege escalation, but it creates avoidable long-term credential exposure. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Recommend an operating-system credential manager or dedicated secret manager instead of agent memory or plaintext JSON. - Do not place reusable credentials in conversational or long-term agent memory. - If file storage is unavoidable: - Create the directory with mode `0700`. - Create the credential file with mode `0600`. - Use atomic creation that refuses to overwrite existing files. - Exclude the file from version control, backups, telemetry, and support bundles. - Document key rotation and immediate revocation procedures. - Prefer short-lived, narrowly scoped tokens over permanent bearer credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/rate-limit.ts:10
Finding
Rate Limiting Fails Open and Uses Untrusted Forwarding Headers as Client Identity<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/rate-limit.ts:10-14, 43-53, 72-75` **Vulnerability Type**: Bypassable and fail-open abuse prevention **Risk Level**: Medium ### Vulnerable Code ```ts function createRedis(): Redis | null { const url = process.env.UPSTASH_REDIS_REST_URL?.trim(); const token = process.env.UPSTASH_REDIS_REST_TOKEN?.trim(); if (!url || !token) return null; return new Redis({ url, token }); } ``` ```ts export async function checkRateLimit( limiter: Ratelimit | null, identifier: string ): Promise<NextResponse | null> { if (!limiter) { console.warn("[rate-limit] Redis unavailable — rate limiting is disabled"); return null; } const { success, limit, remaining, reset } = await limiter.limit(identifier); ``` ```ts export function getClientIp(request: Request): string { const forwarded = request.headers.get("x-forwarded-for"); const real = request.headers.get("x-real-ip"); return forwarded?.split(",")[0]?.trim() ?? real ?? "unknown"; } ``` The unauthenticated voting route uses this value as its sole voter identity: ```ts const ip = getClientIp(request); const rateLimitResponse = await checkRateLimit(getWriteLimiter(), ip); if (rateLimitResponse) return rateLimitResponse; const ipHash = await hashIp(ip); await prisma.vote.create({ data: { submissionId: id, voterIpHash: ipHash, voteType: parsed.data.voteType, }, }); ``` ### Technical Analysis When Redis credentials are absent, `checkRateLimit()` explicitly permits every request. Operational misconfiguration therefore removes all advertised read, write, and upload limits. The client identity function also trusts the first value in `X-Forwarded-For` or `X-Real-IP`. These headers are attacker-controlled unless the application is guaranteed to run behind a trusted proxy that removes incoming versions and generates authoritative replacements. Voting is unauthenticated and duplicate prevention relies on a hash of this derived ...[truncated 1168 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require rate-limiter configuration in production and fail deployment health checks when it is unavailable. - Use a bounded in-process or platform-native fallback limiter rather than silently disabling protection. - Fail closed for costly or integrity-sensitive endpoints when the centralized limiter is unavailable. - Trust forwarding headers only when the immediate peer is an approved reverse proxy. - Configure the proxy to strip client-supplied `X-Forwarded-For` and `X-Real-IP` values and generate authoritative headers. - Prefer hosting-platform-provided verified client-IP metadata. - Require authenticated accounts for voting and uploads. - Enforce one vote per verified account and add behavioral, account-age, and anomaly controls rather than relying only on IP addresses. - Apply separate rate-limit buckets to registration, voting, submission creation, and uploads. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/utils.ts:48
Finding
Deterministic IP Hashing Uses a Public Default Salt<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/utils.ts:48-57` **Vulnerability Type**: Weak pseudonymization of personal data **Risk Level**: Medium ### Vulnerable Code ```ts export async function hashIp(ip: string): Promise<string> { const salt = process.env.IP_HASH_SALT ?? "openclaw-default-salt"; if (!process.env.IP_HASH_SALT) { console.warn("[security] IP_HASH_SALT env var is not set — using default salt"); } const encoder = new TextEncoder(); const data = encoder.encode(salt + ip); const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); } ``` ### Technical Analysis When `IP_HASH_SALT` is not configured, the application hashes IP addresses with the public constant `openclaw-default-salt`. Because IP addresses have a small and enumerable candidate space, an attacker can precompute hashes for likely IPv4 addresses and compare them with database records. Even with a private static salt, plain `SHA-256(salt || IP)` is less appropriate than a keyed message-authentication code. Deterministic values also permit cross-record tracking of the same address for as long as the key remains unchanged. ### Attack Path 1. The application runs without `IP_HASH_SALT`, causing use of the known default. 2. Submission and voting actions store deterministic IP hashes. 3. An attacker obtains the stored hashes through a database disclosure, backup exposure, or excessive internal access. 4. The attacker enumerates candidate IP addresses and computes: `SHA-256("openclaw-default-salt" + candidateIp)`. 5. Matching results reveal likely source IP addresses and correlate activity across records. ### Impact Assessment An attacker with access to hashed records may reverse likely IPv4 addresses and correlate submissions or votes originating from the same address. This weakens voter and submitter privacy and ca ...[truncated 154 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require a high-entropy secret in production and terminate startup if it is missing. - Replace the construction with HMAC-SHA-256 using a server-held key. - Store a key-version identifier so the pseudonymization key can be rotated. - Use purpose-specific keys to prevent correlation with hashes from other systems. - Define and enforce short retention periods for voter and submitter identifiers. - Restrict database and backup access to the minimum required personnel and services. - Consider privacy-preserving anti-abuse designs that do not retain stable IP-derived identifiers longer than necessary. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
Findings (67)

Known Vulnerable Dependency: next==16.1.6 — 16 advisory(ies): CVE-2026-44575 (Next.js has a Middleware / Proxy bypass in App Router applications via segment-p); CVE-2026-45109 (Next.js has a Middleware / Proxy bypass in App Router applications via segment-p); GHSA-2xp9-vwfh-vxw4 (Next.js: Unauthenticated Remote Code Execution in Image Optimization API when AV) +13 more

Critical
Category
Supply Chain
Confidence
98% confidence
Finding
The manifest pins `next` to `16.1.6`, and the provided analysis indicates this exact version has multiple known advisories, including middleware/proxy bypasses and an unauthenticated RCE in the Image Optimization API under certain configurations. In a public leaderboard web application, a vulnerable Next.js framework can expose server-side routes, access controls, or image processing endpoints to remote attackers, making this materially dangerous in context.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
There is a clear mismatch between the declared purpose and the actual code. The description claims functionality for submitting earnings to a public leaderboard with proof and community verification, but the code chunk solely defines HTTP security and CORS headers in a Next.js configuration file. While such configuration could be a supporting detail in a larger app, this specific chunk does not show any behavior related to the declared feature set, and its primary purpose is unrelated infrastructure/security configuration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The declared description focuses narrowly on submitting earnings to the public leaderboard with proof and community verification. However, the code implements a broader leaderboard client: it can register agents, issue/display API keys, read leaderboard rankings, inspect submission details, and access the authenticated user's profile. These are substantive additional capabilities beyond simple submission. The core domain is consistent with the description, but the description does not accurately represent the full behavior of the skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is about leaderboard submission and community verification of earnings. The code chunk does something entirely different: it generates static image assets for a landing page using the Gemini image generation API, then saves those images locally. There is no logic for submitting earnings, uploading proof, interacting with a leaderboard, handling verification, or any related workflow. This is a clear material mismatch in primary purpose and capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is about submitting earnings to a public leaderboard with proof and obtaining community verification. The code instead implements a GET endpoint for retrieving the authenticated agent's own details and submission count. While the submission count is related to leaderboard submissions, the primary behavior is account/profile retrieval, not submission or verification. This is a materially different purpose from the declared functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description suggests a skill whose primary purpose is leaderboard submission of autonomous earnings with proof and community verification. The supplied code does not perform any of those functions. Instead, it creates a new agent account/record, issues an API key, and provides a claim URL. While agent registration could be a prerequisite for later leaderboard participation, this chunk’s actual purpose is onboarding/authentication setup, not earnings submission or verification. That is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The description says the skill submits earnings to the public leaderboard with proof and supports community verification, which implies a write/submission workflow. The code shown does not submit anything, accept proof, or perform verification. Instead, it only handles GET requests to fetch leaderboard standings from the database, applying validation, rate limiting, filtering, grouping, sorting, and pagination. This is a materially different primary purpose, so the description does not accurately represent the code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description focuses on submitting earnings with proof to a public leaderboard and getting community verification. This code chunk does not handle submission creation to the leaderboard at all. Instead, it exposes read and vote endpoints for an existing submission, returns extensive submission metadata, and performs moderation/verification workflow logic by recording votes and auto-updating status. Community verification is related to the description, but the primary behavior of this chunk is vote management and status adjudication rather than earnings submission. That makes the description materially incomplete/misaligned for this specific code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The code does not implement submission of autonomous earnings to a public leaderboard or any community verification workflow. Its primary behavior is a generic image upload API: it accepts a multipart file, validates allowed image types and signatures, rate-limits requests, stores the file publicly via Vercel Blob, and returns the resulting URL. While this could be a supporting component for uploading proof images, the declared description presents a higher-level leaderboard submission capability that is not present in this code chunk. Therefore the description materially overstates and misrepresents the actual behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose describes a leaderboard submission and community verification feature, but the code only implements a GET route that reads and returns the contents of a local markdown file. There is no logic for submitting earnings, generating or attaching proof, contacting a public leaderboard, or performing verification. The actual behavior is materially different from the stated purpose and introduces undeclared filesystem access.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is about submitting earnings to a public leaderboard and community verification. The provided code does not implement submission, proof handling, leaderboard interaction, or verification logic. Instead, it only sets up a PrismaClient for database access and caches it globally in development. While this could be a supporting utility for a larger application, on its own it does not match the declared end-user purpose and introduces undeclared database access capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose is about leaderboard submission and proof-based community verification, but the supplied code does not submit earnings, interact with a leaderboard, handle proofs, or perform any verification workflow. Instead, it provides generic request rate-limiting helpers using Upstash Redis and NextResponse. This is a materially different purpose, not merely a supporting detail for the declared functionality based on the provided chunk alone.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is about submitting earnings to a public leaderboard and community verification. The supplied code chunk does not implement submission, leaderboard interaction, proof generation, verification, or related triggers/permissions. Instead, it contains helper utilities for CSS class merging, formatting currencies and timestamps, and hashing IP addresses. While utility code can support a broader app, this specific chunk introduces handling of IP-derived data and environment-based salting, which is not represented in the declared purpose. Therefore, this code chunk does not accurately match the stated functionality.

Credential Access

High
Category
Privilege Escalation
Content
**⚠️ Save your `api_key` immediately!** You need it for all authenticated requests.

**Recommended:** Save your credentials to memory or `~/.config/openclaw/credentials.json`:

```json
{
Confidence
90% confidence
Finding
The skill recommends storing the API key in ~/.config/openclaw/credentials.json without any warning about filesystem permissions, encryption, or least-privilege handling. A plaintext credential file can be exposed to other local users, backup systems, logs, or other tools running with access to the home directory, enabling impersonation of the agent account.

Lp3

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

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill encourages users to submit detailed agent configuration, tools, and system prompts to a public leaderboard without any warning that such data may contain secrets, internal instructions, or operational details. In this context, public sharing is central to the workflow, so the lack of a privacy warning materially increases the risk of accidental credential leakage and prompt exfiltration.

Ssd 3

Medium
Confidence
98% confidence
Finding
The documentation states that every submission captures the agent's config including model, tools, and system prompt so others can learn. System prompts frequently contain guardrails, proprietary methods, hidden instructions, and sometimes embedded secrets, so encouraging public disclosure creates a direct path for prompt leakage and operational exposure.

External Transmission

Medium
Category
Data Exfiltration
Content
If you've earned money autonomously, submit it in one API call:

```bash
curl -X POST 'https://openclaw-leaderboard-omega.vercel.app/api/v1/submissions' \
  -H 'Content-Type: application/json' \
  -d '{
    "openclawInstanceId": "YOUR_INSTANCE_ID",
Confidence
89% confidence
Finding
The skill directs users to transmit operational and potentially sensitive data to a third-party external service, including descriptions of earnings, proof links, model/tool metadata, and optionally full system prompts. External transmission is expected for a leaderboard skill, but it remains security-relevant because the documentation does not minimize data collection or warn users about the sensitivity and public nature of what they send.

Ssd 3

Medium
Confidence
99% confidence
Finding
The example payload explicitly asks users to send their system prompt, which normalizes disclosure of sensitive internal instructions in a copy-pasteable example. Users often follow examples verbatim, so this materially increases the chance of accidental prompt and secret exposure to an external service.

Ssd 3

Medium
Confidence
98% confidence
Finding
The field documentation explicitly promotes sharing the system prompt to help others learn, but does not explain the security implications of exposing internal behavior instructions. In a public leaderboard context, this can leak proprietary prompting methods and sensitive control logic to anyone browsing submissions.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The screenshot upload flow instructs users to upload proof to public external storage and reuse the returned URL, but it does not warn that screenshots and links may expose personal, financial, customer, or account information. Because proof publication is a core workflow here, omission of a privacy warning can lead users to irreversibly disclose sensitive information to the public internet.

Ssd 3

Medium
Confidence
96% confidence
Finding
Advising users to share config, tools, and prompt information for community trust pressures disclosure of potentially sensitive operational details. This context makes the issue more dangerous because reputation incentives can override users' normal caution and lead to oversharing.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This API spec defines a multipart file upload for proof screenshots, which can affect user privacy and transmit user data, but the description only mentions accepted image types and size limits. There is no warning that uploaded images may contain sensitive information or become accessible via a returned URL.

Session Persistence

Medium
Category
Rogue Agent
Content
**Install locally:**
```bash
mkdir -p ~/.openclaw/skills/leaderboard
curl -s https://openclaw-leaderboard.vercel.app/skill.md > ~/.openclaw/skills/leaderboard/SKILL.md
```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Skill Enumeration

Medium
Category
Agent Snooping
Content
**Install locally:**
```bash
mkdir -p ~/.openclaw/skills/leaderboard
curl -s https://openclaw-leaderboard.vercel.app/skill.md > ~/.openclaw/skills/leaderboard/SKILL.md
```

**Or just read the URL above!**
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
openclaw-skill/tool.js:5

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/generate-images.ts:66

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/lib/auth.ts:20