Back to skill

Security audit

Valiron

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its payment-authorization purpose, but its bundled interceptor template leaves important spend and approval controls unenforced.

Install only if you treat the bundled interceptor as a starter template, not production-ready payment authorization code. Before using it on real payment rails, add runtime schema validation, positive finite money handling, hourly and daily spend accounting, human approval enforcement, idempotency and replay protection, audit logging, and fail-closed behavior backed by durable storage.

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
assets/payment-interceptor.ts:11
Finding
Hourly, Daily, and Human-Approval Spend Controls Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `assets/payment-interceptor.ts:11-18, 47-58` **Vulnerability Type**: Missing enforcement of security-critical payment policy **Risk Level**: High ### Vulnerable Code ```ts export type PolicyRow = { route: Route; authorization: 'allow' | 'allow_with_limits' | 'restricted' | 'deny'; allowedRails: Array<'prod' | 'sandbox'>; maxAmountPerPayment: number; fallbackMode: 'fail-open-guarded' | 'fail-closed'; }; ``` ```ts const row = policy.find((p) => p.route === route); if (!row) return { allow: false, outcome: 'deny', route, reason: 'no policy for route' }; if (!row.allowedRails.includes(req.rail)) { return { allow: false, outcome: 'deny', route, reason: 'rail not allowed for route' }; } if (req.amount > row.maxAmountPerPayment) { return { allow: false, outcome: 'deny', route, reason: 'amount exceeds maxAmountPerPayment' }; } return { allow: row.authorization === 'allow' || row.authorization === 'allow_with_limits', outcome: row.authorization, route, reason: 'policy match', }; ``` ### Technical Analysis The documented payment policy requires `maxAmountPerHour`, `maxAmountPerDay`, and `requireHumanApprovalOver`. The policy validator also requires these fields. However, the interceptor's `PolicyRow` type omits them, and `authorizeOutgoingPayment` only enforces `maxAmountPerPayment`. Consequently, a policy can pass `scripts/validate-payment-policy.mjs` while the runtime interceptor silently ignores cumulative spend limits and human-approval requirements. The implementation also lacks the documented counterparty concentration, idempotency, and replay controls. Secure cumulative-limit enforcement requires atomic accounting because concurrent requests can otherwise independently observe an available balance and collectively exceed the limit. ### Attack Path 1. An attacker or compromised caller submits a payment to a counterparty whose Valiron route maps to `allow` or `allow_with_limits`. ...[truncated 996 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add all mandatory controls to `PolicyRow`, including: - `maxAmountPerHour` - `maxAmountPerDay` - `requireHumanApprovalOver` - Any configured counterparty concentration limit 2. Before returning an allow decision, calculate cumulative spending from a durable, trusted data store. 3. Reserve spend atomically so concurrent requests cannot exceed a limit through race conditions. 4. Require explicit, verifiable approval for payments over `requireHumanApprovalOver`; do not represent pending approval as authorization. 5. Require a unique idempotency key and store its outcome to prevent duplicate execution. 6. Enforce a bounded replay window and bind authorization decisions to the request ID, amount, currency, rail, counterparty, and policy version. 7. Fail closed if accounting, approval, or replay-protection storage is unavailable. 8. Add tests for hourly and daily boundaries, approval thresholds, duplicate requests, concurrent authorization, and storage failures. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
assets/payment-interceptor.ts:48
Finding
Invalid Numeric Payment Amounts Can Bypass the Per-Payment Cap<![CDATA[ ## Vulnerability Details **File Location**: `assets/payment-interceptor.ts:7, 48-50` **Vulnerability Type**: Improper runtime validation of financial input **Risk Level**: Medium ### Vulnerable Code ```ts export type PaymentRequest = { requestId: string; counterpartyAgentId?: string; counterpartyWallet?: string; amount: number; currency: string; rail: 'prod' | 'sandbox'; }; ``` ```ts if (req.amount > row.maxAmountPerPayment) { return { allow: false, outcome: 'deny', route, reason: 'amount exceeds maxAmountPerPayment' }; } ``` ### Technical Analysis The TypeScript `number` annotation provides no runtime validation. The authorization function does not verify that the amount is finite, positive, or valid for the specified currency. A negative amount makes the expression `req.amount > row.maxAmountPerPayment` evaluate to false for a normal non-negative cap. Non-finite values such as `NaN`, if supplied by an internal JavaScript caller or introduced during request processing, also make the comparison false. The request can therefore proceed to an allow result when the matched policy authorization is `allow` or `allow_with_limits`. Using floating-point numbers directly for money also creates precision and boundary risks. Financial values should generally be represented as integer minor units or validated fixed-precision decimals. ### Attack Path 1. A caller constructs an otherwise valid request with a valid counterparty identity and permitted rail. 2. The caller supplies a negative `amount`. An internal JavaScript caller could also supply `NaN` despite the TypeScript declaration. 3. The Valiron lookup returns a route whose policy authorization is `allow` or `allow_with_limits`. 4. The upper-bound comparison evaluates to false and does not deny the request. 5. The function returns `allow: true`. 6. If downstream code trusts this decision without independently validating the amount, the malformed payment reaches accounting or payment-ra ...[truncated 460 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate the request at runtime before making any SDK call: - Require `Number.isFinite(req.amount)`. - Require `req.amount > 0`. - Reject missing, coerced, or nonnumeric values. 2. Represent monetary amounts as integer minor units, such as cents, or use a vetted fixed-precision decimal library. 3. Maintain a currency allowlist and enforce currency-specific decimal precision and minimum/maximum values. 4. Validate `requestId`, counterparty identifiers, currency, and rail through a strict runtime schema. 5. Ensure the downstream payment rail independently validates the amount rather than relying exclusively on this authorization result. 6. Add tests covering negative values, zero, `NaN`, positive and negative infinity, fractional precision, and exact cap boundaries. ]]>
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 (1)

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The function sends `counterpartyAgentId` to `sdk.checkAgent` and `counterpartyWallet` to `sdk.getWalletProfile`, which are network calls involving payment-related identifiers. In this file there is no confirmation prompt, logging, print statement, or explanatory comment/docstring disclosing that external lookups occur.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/decision-policy.md:11