T09 · Insecure Skill Coding Practices
- Location
src/index.ts:277- Finding
Revoked SpendEnvelopes Continue to Pass Local Verification
- Content
View full analysis
{ const { verifyEnvelope } = await import('@pqsafe/agent-pay') const { hexToBytes } = await import('@noble/hashes/utils.js' as string) const overridePubKey = input.dsaPublicKey ? hexToBytes(input.dsaPublicKey) : undefined try { const envelope = verifyEnvelope(input.envelope, overridePubKey) return { valid: true, agent: envelope.agent, issuer: envelope.issuer, validUntil: new Date(envelope.validUntil * 1000).toISOString(), } ``` ### Technical Analysis The production verification path checks the envelope through the local `verifyEnvelope` dependency and immediately returns `valid: true` when cryptographic and temporal validation succeeds. It does not query a revocation registry, inspect a signed revocation snapshot, or otherwise determine whether the envelope has previously been revoked. This conflicts with the declared security model: - `SKILL.md` states that the real-time revocation list is checked on every verification. - `skill.json` states that an envelope will fail `verify_envelope` after revocation. The implementation therefore creates a fail-open authorization condition: a cryptographically valid but revoked payment authorization remains acceptable to any caller relying on this Skill's `valid` result. ### Attack Path 1. An issuer creates a valid signed SpendEnvelope authorizing an Agent to spend funds. 2. An attacker, compromised Agent, or recipient obtains the envelope while it is valid. 3. The issuer invokes `revoke_envelope`, and the remote registry records the revocation. 4. The attacker submits the original envelope to `verify_envelope`. 5. The Skill performs only local signature and time-window validation. 6. Because th ...[truncated 744 chars]- Remediation
View remediation
