Back to skill

Security audit

Kash - Agentic Payment Provider

Security checks for vulnerabilities and agentic risk

Overview

This payment skill is mostly coherent and disclosed, but it has real safeguard gaps that could expose the API key or bypass local spending limits if misconfigured.

Review before installing. Use only a low-privilege Kash agent key, set server-side budgets in the Kash dashboard, keep KASH_API_URL unset unless doing local development, and set numeric budget and confirmation values carefully. Treat local budget and confirmation controls as helpful but not sufficient until the parsing and HTTPS validation issues are fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
tools.ts:19
Finding

Malformed numeric configuration silently disables local spending safeguards

Content
View full analysis
KASH_BUDGET) { return ( `LOCAL_BUDGET_EXCEEDED: Spending $${params.amount} would exceed local KASH_BUDGET of ` + `$${KASH_BUDGET} (session spent: $${sessionSpent.toFixed(4)}). ` + `Tell the user their local budget cap is reached. ` + `They can raise KASH_BUDGET in .env or top up at kash.dev/dashboard/wallets.` ) } // ── Confirmation gate ──────────────────────────────────────────────────────── if (params.amount > SPEND_CONFIRMATION_THRESHOLD && !params.confirmed) { ``` ### Technical Analysis The skill parses `KASH_BUDGET` and `KASH_SPEND_CONFIRMATION_THRESHOLD` with `parseFloat()` but does not verify that the resulting values are finite, nonnegative numbers. A malformed nonempty value such as `x` produces JavaScript's `NaN`. Ordered comparisons involving `NaN` evaluate to `false`. Therefore: - If `KASH_BUDGET=x`, the expression `sessionSpent + params.amount > KASH_BUDGET` is false, silently disabling the advertised local session cap. - If `KASH_SPEND_CONFIRMATION_THRESHOLD=x`, the expression `params.amount > SPEND_CONFIRMATION_THRESHOLD` is false, silently disabling the confirmation requirement for large transactions. - Startup succeeds rather than rejecting the unsafe configuration. The runtime `amount` check also ...[truncated 1315 chars]
Remediation
View remediation
= 0`. - Apply equivalent validation to every `params.amount` received by `kash_spend`. - Reject malformed, infinite, negative, or otherwise unsupported numeric values before performing budget or confirmation comparisons. - Add automated tests for empty strings, alphabetic input, partially numeric strings, `NaN`, positive and negative infinity, negative values, zero, and valid decimal values. Example hardening pattern: ```ts function parseNonNegativeNumber(name: string, raw: string): number { const value = Number(raw) if (!Number.isFinite(value) || value < 0) { throw new Error(`[kash-skill] ${name} must be a finite, nonnegative number`) } return value } const KASH_BUDGET = process.env.KASH_BUDGET === undefined ? null : parseNonNegativeNumber('KASH_BUDGET', process.env.KASH_BUDGET) const SPEND_CONFIRMATION_THRESHOLD = parseNonNegativeNumber( 'KASH_SPEND_CONFIRMATION_THRESHOLD', process.env.KASH_SPEND_CONFIRMATION_THRESHOLD ?? '5.00' ) ``` At the tool boundary, enforce: ```ts if (!Number.isFinite(params.amount) || params.amount <= 0) { return 'ERROR: amount must be a finite number greater than 0' } ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
tools.ts:47
Finding

API endpoint allowlist permits plaintext transmission of the Kash credential

Content
View full analysis
url.hostname === d || url.hostname.endsWith(`.${d}`) ) if (!isTrusted) { throw new Error( `[kash-skill] KASH_API_URL points to untrusted domain: "${url.hostname}".\n` + 'Only api.kash.dev and localhost are allowed.\n' + 'Remove KASH_API_URL from .env or use a trusted endpoint.' ) } } catch (e: any) { if (e.message.startsWith('[kash-skill]')) throw e throw new Error(`[kash-skill] KASH_API_URL is not a valid URL: ${KASH_API_URL}`) } } ``` The accepted URL is subsequently used for credential-bearing requests: ```ts const res = await fetch(`${KASH_API_URL}/api/agents/${KASH_AGENT_ID}/spend`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-kash-key': KASH_KEY!, }, ``` ```ts const res = await fetch(`${KASH_API_URL}/api/agents/${KASH_AGENT_ID}/balance`, { headers: { 'x-kash-key': KASH_KEY! }, }) ``` ### Technical Analysis The endpoint validation checks only `url.hostname`. It does not verify `url.protocol`, despite `SECURITY.md` stating that the credential is sent over HTTPS. As a result, a value such as `http://api.kash.dev` passes the hostname allowlist. Both balance and spending requests then include `KASH_KEY` in the `x-kash-key` header over an unencrypted HTTP connection. The allowlist also accepts subdomains through `hostname.endsWith()`, even though the documented production destination is specifically `api.kash.dev`. The confirmed credential-exposure pat ...[truncated 1280 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tools.ts (reported line 61)May include surrounding context.

ts
throw new Error(
        `[kash-skill] KASH_API_URL points to untrusted domain: "${url.hostname}".\n` +
        'Only api.kash.dev and localhost are allowed.\n' +
        'Remove KASH_API_URL from .env or use a trusted endpoint.'
      )
    }
  } catch (e: any) {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tools.ts (reported line 93)May include surrounding context.

ts
throw new Error(
        `[kash-skill] KASH_API_URL points to untrusted domain: "${url.hostname}".\n` +
        'Only api.kash.dev and localhost are allowed.\n' +
        'Remove KASH_API_URL from .env or use a trusted endpoint.'
      )
    }
  } catch (e: any) {

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill uses sensitive environment variables and network-backed payment capabilities, but it does not declare a restrictive tool scope such as permissions or allowed-tools. That omission increases the attack surface because a host agent may expose broader capabilities than intended, making it easier for prompt injection or skill misuse to access secrets or trigger external payment actions outside a clearly bounded interface.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
tools.ts:19