Back to skill

Security audit

skill-by-test

Security checks for vulnerabilities and agentic risk

Overview

This sandbox payment-link skill is not malicious, but its helper script sends unvalidated payment data to a local HTTP API and does not actually use the declared API key.

Use this only for local sandbox testing. Do not provide production API keys, real cardholder data, real customer data, or live webhook endpoints. Before any production use, require schema validation, remove the hard-coded placeholder key, use the declared environment secret correctly, authenticate requests, and move payment traffic to an authenticated HTTPS or otherwise secured local channel.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Note
Location
scripts/test-scrpt.js:1
Finding
Hard-Coded and Ineffective API Credential Handling<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test-scrpt.js`, lines 1-9 and 59-64 **Vulnerability Type**: Hard-coded credential and ineffective authentication **Risk Level**: Low ### Vulnerable Code ```javascript const API_KEY = 'abc'; const BASE = "http://localhost:4000/v1"; if (!API_KEY) { console.error("Missing"); process.exit(1); } ``` The subsequent request does not use `API_KEY`: ```javascript const res = await fetch(url, { method, headers: { "Content-Type": "application/json", }, ...(body ? { body } : {}), }); ``` ### Technical Analysis The script embeds a credential-like value directly in source code rather than reading the `TEST_API_KEY` environment variable declared in `SKILL.md`. The startup check only verifies that the hard-coded string is nonempty and therefore cannot detect a missing runtime credential. Furthermore, `API_KEY` is never included in the outbound request. Consequently, the script's credential check provides no authentication protection and creates a false impression that API authentication is enforced. The current value, `abc`, appears to be a placeholder; there is no evidence that it is a valid secret. Nevertheless, the implementation pattern is unsafe because replacing it with a valid key would expose that key to anyone with source-code access. ### Attack Path 1. An operator replaces the placeholder with a valid API credential while preserving the existing implementation. 2. The credential becomes part of the source file and may be exposed through package distribution, source control, backups, logs, or filesystem access. 3. A party that obtains the source can recover and reuse the credential against services where it is valid. 4. Independently, because the script never sends the key, requests are made without application-layer authentication. If the local endpoint accepts unauthenticated requests, callers can perform the supported payment-link operation without the intended credential c ...[truncated 495 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the hard-coded value and load the credential from the declared environment variable: ```javascript const API_KEY = process.env.TEST_API_KEY; ``` - Fail closed when the environment variable is absent or empty. - Send the credential only through the authorization mechanism documented by the API, such as an `Authorization` header. - Never print the key or include it in error messages. - Keep real credentials out of source control and distributed Skill packages. - Use short-lived, sandbox-only credentials with narrowly scoped permissions. - Add automated secret scanning and a test verifying that authenticated requests contain the required authorization header. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/test-scrpt.js:3
Finding
Payment API Requests Use Unencrypted HTTP Transport<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test-scrpt.js`, lines 3 and 57-65 **Vulnerability Type**: Plaintext transport of payment-link request data **Risk Level**: Low ### Vulnerable Code ```javascript const BASE = "http://localhost:4000/v1"; ``` ```javascript const url = `${BASE}${resolvedPath}${queryString}`; const body = method !== "GET" ? buildBody(type, data) : undefined; const res = await fetch(url, { method, headers: { "Content-Type": "application/json", }, ...(body ? { body } : {}), }); ``` ### Technical Analysis The script transmits user-supplied payment-link data to an HTTP endpoint without transport encryption or peer authentication. This conflicts with the Skill's stated security rule to use secure HTTPS endpoints only. The loopback destination reduces exposure compared with a remote plaintext endpoint because ordinary network intermediaries cannot directly observe loopback traffic. However, HTTP still does not authenticate the receiving process. A malicious or compromised local process that binds to port `4000`, or a local proxy or routing manipulation affecting the endpoint, could receive the request and return attacker-controlled payment-link data. The risk would increase substantially if `BASE` were later changed from `localhost` to a remote host without also enforcing HTTPS. ### Attack Path 1. The legitimate local service on port `4000` is unavailable, displaced, or not started. 2. An attacker with sufficient local access starts a listener on `localhost:4000`. 3. A user invokes the Skill with payment-link data. 4. The script sends the JSON request to the attacker's unauthenticated HTTP listener. 5. The listener captures the submitted data and can return a fabricated JSON response, including an attacker-controlled payment URL. 6. Because the script prints the response without authenticating its source, the fabricated response may be presented as if it came from the expected service. ...[truncated 424 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use an HTTPS endpoint and retain normal certificate and hostname verification. - If communication must remain strictly local, consider a permission-restricted Unix domain socket or another authenticated inter-process communication mechanism. - Reject configured remote endpoints that do not use HTTPS. - Authenticate requests and responses at the application layer rather than relying only on the destination port. - Bind the local service according to least privilege and restrict which users can communicate with it. - Validate returned payment URLs and reject non-HTTPS URLs or unexpected hosts before presenting them to users. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/test-scrpt.js:42
Finding
Unvalidated Arbitrary Fields Are Forwarded to the Payment API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test-scrpt.js`, lines 42-65 **Vulnerability Type**: Missing input validation and unrestricted request-body construction **Risk Level**: Medium ### Vulnerable Code ```javascript function buildBody(type, data) { if (!data) return undefined; const { id, ...rest } = data; // always strip id — it lives in the URL return Object.keys(rest).length > 0 ? JSON.stringify(rest) : undefined; } async function main() { const raw = process.argv[2]; if (!raw) throw new Error("No payload provided. Pass JSON as first argument."); const { type, data } = JSON.parse(raw); if (!type) throw new Error("Missing 'type' in payload."); const endpoint = ENDPOINTS[type]; if (!endpoint) throw new Error(`Unsupported type: "${type}"`); const { method, path } = endpoint; const resolvedPath = buildUrl(path, data); const queryString = method === "GET" ? buildQueryString(type, data) : ""; const url = `${BASE}${resolvedPath}${queryString}`; const body = method !== "GET" ? buildBody(type, data) : undefined; const res = await fetch(url, { method, headers: { "Content-Type": "application/json", }, ...(body ? { body } : {}), }); ``` ### Technical Analysis The script parses attacker-controllable command-line JSON and verifies only that a supported `type` is present. For the supported POST operation, `buildBody` removes the `id` property but serializes every other supplied property without an allowlist or schema validation. The implementation does not enforce the validation controls described in `SKILL.md`, including positive numeric amounts, ISO currency codes, URL format, email format, metadata structure, duplicate-product prevention, or sanitization. It also does not limit payload depth or size. This creates a validation-boundary failure: the Skill presents itself as the component responsible for validating payment-link inputs but delegates arbitrary object ...[truncated 1652 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Define a strict schema for every supported operation and reject unknown properties. - Verify that `data` is a plain object before destructuring or serializing it. - Validate numeric amounts as finite, positive values within explicit minimum and maximum bounds. - Restrict currencies to supported uppercase ISO 4217 codes. - Parse and validate URLs, require HTTPS, and allowlist acceptable redirect hosts where appropriate. - Validate email addresses and constrain all string lengths. - Validate metadata recursively, including permitted keys, value types, depth, and total serialized size. - Detect duplicate product entries and verify product identifiers through a trusted source. - Set an explicit maximum command-line payload and request-body size. - Retain equivalent validation in the downstream API; the Skill's validation must not replace server-side enforcement. - Add negative tests covering unknown fields, invalid types, negative and non-finite amounts, malformed URLs, unsupported currencies, deep objects, and oversized payloads. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (2)

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This skill is centered on payment-link creation, webhook handling, and API-driven network operations, but it does not present a prominent user-facing warning about sensitive payment-related data, external requests, and the risk of exposing secrets or interacting with financial workflows. In this context, missing warnings can lead users or downstream agents to provide sensitive data unsafely, mis-handle webhook endpoints, or assume the skill is safer than it is, increasing the chance of credential leakage or unintended financial actions.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code performs a network POST request and may transmit arbitrary fields from the user-supplied `data` object, but there is no confirmation prompt, warning message, or explanatory comment disclosing that payload data will be sent over HTTP. The only visible output is the response after the request completes, which does not warn the user beforehand about data transmission.

Static analysis

No suspicious patterns detected.