Back to skill

Security audit

Metered API Marketplace

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent metered API template, but it needs Review because its payment, credential, and deployment flows include under-disclosed external routing and billing-security flaws.

Install only if you intend to operate a public metered API and are prepared to review and harden it first. Replace the hardcoded api.vms0.com endpoint with your own confirmed deployment unless you deliberately choose that hosted service and trust its operator. Before production use, require strong webhook secrets, disable or guard the demo provider, protect admin routes, fix idempotency and failed-charge behavior, pin dependencies with lockfiles, and avoid sending sensitive lead, contract, or business data to any endpoint you do not control.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
references/integration_prompt.md:1
Finding
Agent Integration Instructions Redirect Requests to a Hardcoded External Paid Service<![CDATA[ ## Vulnerability Details **File Location**: `references/integration_prompt.md:1-8` **Vulnerability Type**: Hardcoded third-party service redirection in agent instructions **Risk Level**: High ### Vulnerable Code Snippet ```md # Agent Integration Prompt (copy/paste) You are integrating a paid, metered API called **VMS0 Metered API Marketplace**. Goal: call deterministic transformers (no LLM dependency) via a signed API key. Each request costs **$0.25** and is deducted from prepaid balance. ## Base URL `https://api.vms0.com` ``` ### Technical Analysis The primary Skill describes a template that users can deploy and operate themselves. However, the provided copy-and-paste integration prompt does not use a deployment placeholder or instruct the user to enter the URL of their own instance. Instead, it directs the agent to send requests to the fixed external domain `api.vms0.com`. The instructions identify this service as paid and direct the integrating agent to submit signed API requests containing transformation inputs. Depending on the selected transformer, those inputs may include lead records, advertising content, landing-page material, or contract text. The ownership and data-handling policy of the hardcoded service are not explained. The redirection is also inconsistent with the surrounding workflow, which presents the bundled code as a self-hosted deployment template. Although HMAC request signatures do not directly reveal the API secret, the external service receives: - The public API-key identifier. - Signed requests and timestamps. - Complete transformation request bodies. - Request identifiers and associated operational metadata. - Any commercially sensitive information included in the transformation input. ### Attack Path 1. A user or agent copies the supplied integration prompt. 2. The agent treats `https://api.vms0.com` as the required service endpoint. 3. The user supplies or configures an API key and prepaid balance for that s ...[truncated 867 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the fixed domain with an explicit deployment placeholder: ```md ## Base URL `<YOUR_DEPLOYMENT_BASE_URL>` ``` 2. Require the user to confirm the endpoint before an agent sends any request. 3. Clearly distinguish between: - A user-owned self-hosted deployment. - An optional third-party hosted service. 4. If a hosted service is intentionally offered, disclose: - Service ownership. - Pricing and billing behavior. - Data retention and privacy policies. - Whether inputs are logged or reused. - Security and incident-response contacts. 5. Recommend testing the endpoint identity and TLS configuration before submitting sensitive data. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server/index.js:25
Finding
Missing Demo Webhook Secret Allows Forged Balance Credits<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server/index.js:25, 260-275` **Additional Location**: `scripts/nextjs-starter/pages/api/v1/payments/webhook/[provider].js:27-37` **Vulnerability Type**: Fail-open webhook authentication with an empty HMAC key **Risk Level**: Critical ### Vulnerable Code Snippet Standalone server: ```js const WEBHOOK_SHARED_SECRET = env('WEBHOOK_SHARED_SECRET', ''); ``` ```js // Webhook: demo provider style (shared secret + normalized payload) app.post('/v1/payments/webhook/:provider', async (req, reply) => { const provider = String(req.params.provider); const sig = req.headers['x-webhook-signature']; if (!sig) return reply.code(401).send({ ok: false, error: 'missing_webhook_signature' }); const raw = req.rawBody ?? Buffer.from(''); const expected = hmacHex(WEBHOOK_SHARED_SECRET, raw.toString('utf8')); const expBuf = Buffer.from(expected, 'hex'); const gotBuf = Buffer.from(String(sig), 'hex'); if (expBuf.length !== gotBuf.length || !crypto.timingSafeEqual(expBuf, gotBuf)) { return reply.code(403).send({ ok: false, error: 'bad_webhook_signature' }); } const { event_id, api_key, gross_cents, chain, txid } = req.body ?? {}; if (!event_id || !api_key || !gross_cents) return reply.code(400).send({ ok: false, error: 'missing_fields' }); ``` The Next.js implementation has the same insecure default: ```js if (provider === 'demo') { const sig = header(headers, 'x-webhook-signature'); if (!sig) throw Object.assign(new Error('missing_webhook_signature'), { statusCode: 401 }); const expected = hmacHex(env('WEBHOOK_SHARED_SECRET', ''), rawText); if (!timingSafeHexEqual(expected, sig)) throw Object.assign(new Error('bad_webhook_signature'), { statusCode: 403 }); const { event_id, api_key, gross_cents, chain, txid } = body ?? {}; if (!event_id || !api_key || !gross_cents) throw Object.assign(new Error('missing_fields'), { statusCode: 400 }); return { event_id, api_key, gross_cen ...[truncated 2350 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail startup if the shared secret is absent: ```js if (!WEBHOOK_SHARED_SECRET) { throw new Error('Missing WEBHOOK_SHARED_SECRET'); } ``` 2. Enforce a minimum randomly generated secret length, such as at least 32 bytes of entropy. 3. In the Next.js handler, reject the demo provider when the secret is not configured: ```js const secret = env('WEBHOOK_SHARED_SECRET', ''); if (!secret) { throw Object.assign(new Error('demo_webhook_disabled'), { statusCode: 503 }); } ``` 4. Disable the demo webhook entirely in production. 5. Reject all provider names except an explicit allowlist. 6. Use distinct secrets per provider and environment. 7. Validate that credited events represent a final, irreversible payment state. 8. Add monitoring and alerts for unusually large credits, repeated failures, and unknown provider paths. 9. Consider maximum credit limits and manual review thresholds. 10. Rotate any webhook secrets after correcting the deployment because prior deployment state may be unknown. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/server/index.js:204
Finding
Idempotency Keys Are Not Bound to Request Content, Allowing Unpaid Transformations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server/index.js:204-247` **Additional Location**: `scripts/nextjs-starter/pages/api/v1/transform/[name].js:51-97` **Vulnerability Type**: Billing bypass through incomplete idempotency validation **Risk Level**: Medium ### Vulnerable Code Snippet ```js if (requestId) { const { rows } = await client.query( 'SELECT id, cost_cents FROM usage WHERE api_key_id=$1 AND transformer=$2 AND request_id=$3', [apiKeyId, name, requestId] ); if (rows[0]) { await client.query('COMMIT'); // idempotent: do not charge again after = before; usageId = rows[0].id; } } if (!usageId) { after = before - cost; await upsertBalanceCents(client, apiKeyId, after); usageId = id('use'); await client.query( 'INSERT INTO usage(id, api_key_id, route, transformer, request_hash, request_id, cost_cents) VALUES($1,$2,$3,$4,$5,$6,$7)', [usageId, apiKeyId, '/v1/transform/:name', name, requestHash, requestId, cost] ); await client.query('COMMIT'); } // Pure-function transform (no external calls, no storage) let data; try { data = fn(req.body ?? {}); } catch (e) { return reply.code(500).send({ ok: false, error: 'transformer_failed' }); } ``` ### Technical Analysis The application calculates and stores a `request_hash`, but duplicate detection only compares: - API-key identifier. - Transformer name. - Caller-supplied `request_id`. When a matching request ID exists, the code skips the charge without loading or comparing the stored `request_hash`. It then executes the transformer against the current request body instead of returning a previously stored result. Consequently, the caller-controlled idempotency identifier acts as a reusable billing exemption. Idempotency should mean that an exact retry of the same operation returns the same result without repeating side effects. It should not allow different operations to share the same billing record. The same logic is present i ...[truncated 1199 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Load the stored request hash when checking an idempotency key: ```sql SELECT id, request_hash, cost_cents FROM usage WHERE api_key_id=$1 AND transformer=$2 AND request_id=$3 ``` 2. Compare the stored hash with the hash of the current raw body. 3. Return `409 Conflict` if the same idempotency key is reused with different content: ```js if (existing.request_hash !== requestHash) { return reply.code(409).send({ ok: false, error: 'idempotency_key_reused_with_different_payload' }); } ``` 4. Store the original transformation result, or a stable reference to it, and return that result for exact retries. 5. Do not execute the transformer again for an idempotent replay. 6. Retain the database uniqueness constraint on API key, transformer, and request ID. 7. Document the permitted format, scope, and expiration policy for idempotency keys. 8. Add automated tests covering: - Exact retry with the same body. - Reuse with a different body. - Concurrent requests using the same key. - Reuse across different transformers and API keys. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/server/package.json:8
Finding
Dependency Installation Is Not Reproducible Due to Mutable Version Ranges and Missing Lockfiles<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server/package.json:8-13` **Additional Locations**: `scripts/nextjs-starter/package.json:8-13`, `SKILL.md:38-42` **Vulnerability Type**: Unlocked and mutable third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code Snippet Standalone server manifest: ```json "dependencies": { "dotenv": "^16.4.5", "fastify": "^5.2.1", "@fastify/rate-limit": "^10.2.0", "pg": "^8.13.3" } ``` Next.js manifest: ```json "dependencies": { "next": "^14.2.0", "react": "^18.2.0", "react-dom": "^18.2.0", "pg": "^8.13.3" } ``` Installation instruction: ```md Typical run: - `cd scripts/server` - `npm install` ``` ### Technical Analysis The project does not include an audited package lockfile, while its direct dependencies use caret ranges. A caret range allows package managers to select newer compatible releases than those visible in the manifest. As a result, two installations performed at different times can resolve different direct or transitive dependency graphs. The code that executes during installation and at runtime can therefore change after the Skill itself has been reviewed. No confirmed malicious package was identified in the reviewed manifests. The risk arises from non-reproducible resolution and the lack of a fixed, reviewable dependency graph. ### Attack Path 1. A user follows the documented `npm install` instruction. 2. npm resolves the mutable version ranges and their current transitive dependencies. 3. A later permitted release, compromised package version, or newly introduced vulnerable transitive dependency is selected. 4. Unreviewed package code is installed and may execute through package lifecycle scripts or during application runtime. 5. The deployed service consequently differs from the version that was originally audited. ### Impact Assessment The exact impact depends on the behavior and privileges of a compromised or vulnerable dependency. Because t ...[truncated 479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate and commit a reviewed `package-lock.json` for each independently deployable application. 2. Use exact dependency versions rather than caret ranges where practical. 3. Replace the installation instruction with: ```sh npm ci ``` 4. Run dependency vulnerability scanning in continuous integration. 5. Configure automated dependency updates to open reviewed pull requests rather than updating production implicitly. 6. Review transitive dependency and lifecycle-script changes before accepting lockfile updates. 7. Use a trusted npm registry configuration and enable package provenance verification where available. 8. Rebuild and redeploy only from the committed manifest and lockfile. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (34)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is for infrastructure that monetizes and operates a public API microservice. The supplied code does something materially different: it exports a set of deterministic content/analysis transformers such as ad copy generation, lead scoring, contract risk scanning, pricing heuristics, and funnel math. There is no server setup, request handling, authentication, persistent storage, webhook processing, payment integration, ledger/accounting, rate limiting, or abuse prevention. In fact, the file comments state it should remain deterministic with no network or storage. This is a strong description-behavior mismatch because the primary purpose and capabilities do not align.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a full monetized public API platform with authentication, metering, billing, balances, and payment processing. The supplied code only implements a simple health endpoint returning a static success response. This is a materially different primary purpose and lacks essentially all of the described capabilities, so the description does not accurately represent the code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is for infrastructure that monetizes and secures a public API service. This code chunk instead exports a collection of pure transformation functions for business/marketing/sales/support analysis and copy generation. It operates only on provided inputs and returns deterministic outputs. There is no evidence of networking, HTTP routing, authentication, billing, webhook processing, balance storage, ledger accounting, or abuse prevention. The primary purpose is materially different from the declared purpose, so this is a clear mismatch.

Ae1

High
Category
analysis-evasion
Content
Use `scripts/server/admin/create_key_pg.js` (or the admin HTTP endpoint) to create a key and starting balance.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
78% confidence
Finding
The skill advertises and orchestrates infrastructure with access to environment configuration, secrets, payment settings, and deployment workflows, but it does not declare any explicit tool scope or permissions boundaries. In agent ecosystems, missing scope declarations can lead to overbroad access during execution, making secret exposure or unintended privileged operations more likely.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The activation language is broad enough that an agent may invoke this skill in loosely related monetization or API scenarios without clear boundaries. Because the skill covers payments, API auth, billing, webhooks, and deployment, overbroad triggering increases the chance of accidental exposure of secrets, unsafe infrastructure changes, or inappropriate handling of financial workflows.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The documentation inconsistently states that the reference server uses SQLite and later says it uses Postgres. For billing, balances, webhook idempotency, and financial ledger logic, storage ambiguity is dangerous because operators may deploy with the wrong database guarantees, migration assumptions, or transaction semantics, causing data integrity or accounting failures.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The integration prompt clearly instructs an agent to call a paid API where each request deducts funds from a prepaid balance, but it does not require explicit user confirmation before incurring charges. In an agent setting, this can lead to unauthorized or unexpected spending, especially if the agent autonomously retries, probes transformers, or performs background balance checks and billable calls without the user's informed consent.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The README exposes a production deployment pattern that includes highly sensitive secrets and publicly documented admin endpoints without any warning about access controls, secret storage, or exposure risks. In a monetized public API skill with payment webhooks, balances, and admin key creation, this omission materially increases the chance that deployers will misconfigure authentication, leak credentials, or expose privileged routes to the internet.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The migrate() function executes CREATE TABLE and CREATE UNIQUE INDEX statements against the configured database, which changes persistent system state. The code contains no confirmation prompt, user-facing logging, or explanatory comment/docstring warning that it will modify the database schema.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code performs a safety-sensitive operation by generating a new API secret, persisting it, and returning it in the response. There is no confirmation prompt, user-facing log/message, comment, or docstring warning that the endpoint creates credentials and exposes the secret once, which is the kind of sensitive operation covered by missing user warnings for code files.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code generates a new API credential, stores it in the database, and prints the secret to stdout, but it includes no comment, prompt, or user-facing warning about handling sensitive credentials. Because the script accesses database credentials via environment configuration and emits a newly created secret, users are not explicitly warned about the sensitivity of the operation or the need to protect terminal logs/output.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The endpoint deducts credits and records usage before executing the transformer, but if the transformer throws, the handler returns an error without reversing the charge. This creates a fail-open billing condition where callers can be charged for unsuccessful requests, causing ledger integrity issues and potential customer harm in a metered API product.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code creates credentials and immediately returns the raw API secret in the HTTP response, which is a sensitive operation affecting credential handling. The file has no user-facing log, confirmation prompt, or explanatory comment warning callers that the secret will be disclosed once and must be handled securely.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The manifest describes infrastructure for exposing and monetizing OpenClaw skills as a metered public API with auth, logging, pricing, balances, and crypto top-ups. This file instead defines dozens of domain-specific content, sales, legal-risk, support, and trading signal transformers, which are end-user business capabilities rather than implementation details of API metering infrastructure.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The contract-risk-analyzer parses contract text and produces legal negotiation guidance. Legal document analysis is not an obvious requirement for API-key auth, usage metering, prepaid balances, or payment webhook handling, and the manifest does not mention legal-analysis features.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The arbitrage-spread-detector computes trading opportunities from buy/sell legs, and this file also later exposes a simple arbitrage signal. Trading signal generation is unrelated to building and operating a metered public API endpoint and is not declared in the manifest's scope.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
This transformer evaluates buy/sell/fees and outputs a go/no-go trading signal. That capability is a domain-specific financial-analysis feature, not an implementation necessity for API metering, credit ledgers, rate limiting, or payment processing.

Vague Triggers

Low
Confidence
81% confidence
Finding
This markdown file consists of imperative deployment steps headed simply as "Deploy (Vercel)" and "3) Deploy" without clarifying the invocation context, audience, or exclusions. In a skill setting, such broad wording can make it unclear when these instructions should be applied versus when they should not, which matches the vague-trigger category for markdown files.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"start": "next start"
  },
  "dependencies": {
    "next": "^14.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "pg": "^8.13.3"
Confidence
93% confidence
Finding
The package manifest uses a caret range for Next.js instead of pinning an exact version, which allows different installs to resolve to different releases over time. In a public API/payment-processing skill, that weakens supply-chain control and can unintentionally introduce vulnerable or behavior-changing framework updates.

Unverifiable Dependency: next has 16 known advisory(ies) (CVE-2025-30218 (Next.js may leak x-middleware-subrequest-id to external hosts); CVE-2021-43803 (Unexpected server crash in Next.js.); CVE-2026-44575 (Next.js has a Middleware / Proxy bypass in App Router applications via segment-p) +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
82% confidence
Finding
The manifest does not pin the exact Next.js version, so it is impossible to verify from this file whether the deployed package includes a release affected by known advisories. In an exposed API service, uncertainty around framework vulnerability status is risky because middleware, routing, and request-handling flaws can be remotely reachable.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "next": "^14.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "pg": "^8.13.3"
  }
Confidence
90% confidence
Finding
React is specified with a floating caret version, so the installed dependency may vary between environments or over time. While this file alone does not prove exploitation, unpinned frontend dependencies increase supply-chain and patch-verification risk, especially in an internet-facing service stack.

Unverifiable Dependency: react has 2 known advisory(ies) (CVE-2013-7035 (Cross-Site Scripting in react); GHSA-hg79-j56m-fxgv (Cross-Site Scripting in react)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
74% confidence
Finding
Because react is unpinned, the exact installed version cannot be verified against known advisories from the manifest alone. This is mainly a dependency-hygiene issue here, though it still matters because the service may expose web interfaces where frontend library flaws could affect users or operators.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "next": "^14.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "pg": "^8.13.3"
  }
}
Confidence
90% confidence
Finding
react-dom is not pinned to an exact release, so builds may consume different package versions without explicit review. That creates avoidable uncertainty around security posture and makes incident response or rollback harder if a bad release is published.

Unverifiable Dependency: react-dom has 1 known advisory(ies) (CVE-2018-6341 (Cross-Site Scripting in react-dom)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
73% confidence
Finding
The unpinned react-dom entry prevents confirming whether the installed version is impacted by known advisories. The risk is limited by the lack of exploit context in this file, but exact-version ambiguity still undermines security verification for any rendered web UI.

Static analysis

No suspicious patterns detected.