T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:1770
- Finding
- Escrow Funding Proceeds Without Verified Bilateral Signatures## Vulnerability Details **File Location**: `SKILL.md`, lines 1770-1790 **Vulnerability Type**: Missing authorization and signature verification before financial commitment **Risk Level**: High ### Vulnerable Code ```python def finalize_contract(self, contract: dict) -> dict: """Sign and activate a negotiated contract.""" # Sign the contract signature = self.signer.sign_contract(contract) contract["parties"]["consumer" if self.agent_id == contract["parties"]["consumer"]["agent_id"] else "provider"]["claim_chain_id"] = signature["claim_chain_id"] contract["parties"]["consumer" if self.agent_id == contract["parties"]["consumer"]["agent_id"] else "provider"]["signed_at"] = signature["signed_at"] # Create escrow escrow_result = self.escrow_mgr.create_contract_escrow(contract) escrow_id = escrow_result["escrow_id"] contract["escrow"]["escrow_id"] = escrow_id # Activate all SLA monitors activation = self.tracker.activate_all_obligations(contract) # Update fingerprint with all runtime IDs contract["fingerprint"] = self.signer.fingerprint(contract) ``` ### Technical Analysis The lifecycle manager signs the contract only as the currently authenticated agent and then immediately creates the escrow. It does not invoke `verify_counterparty_signature()`, even though that method is defined elsewhere in the guide for bilateral signature verification. Consequently, there is no enforcement that: - The counterparty signed the contract. - The counterparty signature belongs to the expected agent. - Both parties signed the same canonical contract fingerprint. - The counterparty signature was created before escrow funding. - The contract remained unchanged between signing and activation. The contract is also mutated after the local signature is generated by adding signature, escrow, and runtime identifiers. This requires a cle ...[truncated 1546 chars]
- Remediation
- ## Remediation Suggestions 1. Define an immutable canonical contract payload that excludes mutable runtime fields such as escrow IDs and SLA monitor IDs. 2. Calculate one canonical fingerprint from that payload. 3. Require independent signatures from both expected party identities over that exact fingerprint. 4. Call `verify_counterparty_signature()` and verify the chain owner, contract ID, fingerprint, signature status, and expected counterparty identity before creating escrow. 5. Reject activation if either signature is absent, expired, revoked, malformed, or associated with another contract version. 6. Bind escrow creation to the verified fingerprint and signature-chain identifiers. 7. Add an explicit contract state machine such as `draft → locally_signed → bilaterally_signed → funded → active`. 8. Require a human or policy-engine approval for deposits above a configured financial threshold. 9. Use an idempotency key based on the contract ID and signed fingerprint to prevent duplicate escrow creation.
