T09 · Insecure Skill Coding Practices
- Location
SKILL.md:1770- Finding
Escrow Funding Proceeds Without Verified Bilateral Signatures
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 1770-1790
Vulnerability Type: Missing authorization and signature verification before financial commitment
Risk Level: HighVulnerable 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
View remediation
Remediation Suggestions
- Define an immutable canonical contract payload that excludes mutable runtime fields such as escrow IDs and SLA monitor IDs.
- Calculate one canonical fingerprint from that payload.
- Require independent signatures from both expected party identities over that exact fingerprint.
- Call
verify_counterparty_signature()and verify the chain owner, contract ID, fingerprint, signature status, and expected counterparty identity before creating escrow. - Reject activation if either signature is absent, expired, revoked, malformed, or associated with another contract version.
- Bind escrow creation to the verified fingerprint and signature-chain identifiers.
- Add an explicit contract state machine such as
draft → locally_signed → bilaterally_signed → funded → active. - Require a human or policy-engine approval for deposits above a configured financial threshold.
- Use an idempotency key based on the contract ID and signed fingerprint to prevent duplicate escrow creation.
