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. ]]>
