T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:543
- Finding
- Unauthenticated ACP Webhook Permits Forged Payment and Fulfillment Operations<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 543-575 **Vulnerability Type**: Unauthenticated webhook and missing payment-event verification **Risk Level**: High ### Vulnerable Code ```python # Webhook handler for incoming ACP purchase events @app.route("/webhooks/acp", methods=["POST"]) def acp_webhook(): event = request.json event_type = event.get("type") if event_type == "checkout.initiated": result = acp_bridge.handle_acp_purchase(event.get("intent")) return jsonify(result) elif event_type == "payment.completed": # Stripe has confirmed payment -- now fulfill via GreenHelix intent_id = event["intent"]["id"] payment_id = event["intent"]["greenhelix_payment_id"] # Execute your GreenHelix-backed service service_result = client.execute("search_services", { "service_id": event["intent"]["metadata"]["greenhelix_service_id"], }) fulfillment = acp_bridge.handle_acp_fulfillment( acp_intent_id=intent_id, greenhelix_payment_id=payment_id, result=service_result, ) return jsonify(fulfillment) return jsonify({"status": "ignored"}), 200 ``` The downstream purchase operation creates GreenHelix payment state from fields supplied by the webhook: ```python payment_intent = self.client.execute("create_payment_intent", { "payer_agent_id": buyer_agent_id, "payee_agent_id": self.agent_id, "amount": amount_usd, "description": ( f"ACP checkout for service {greenhelix_service_id}. " f"ACP intent: {acp_intent.get('id', 'unknown')}" ), "metadata": json.dumps({ "protocol": "acp", "acp_intent_id": acp_intent.get("id"), "stripe_payment_intent": acp_intent.get("stripe_pi_id"), }), }) ``` ### Technical Analysis The webhook handler parses and trusts the request body without authenticating its origin. It does not verify a Stripe or ACP w ...[truncated 2621 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Verify the webhook signature against the raw HTTP request body before JSON parsing. Use the official provider SDK and `stripe_webhook_secret`; do not implement ad hoc signature comparison. 2. Enforce a strict timestamp tolerance to prevent replay of old signed messages. 3. Persist processed provider event IDs and reject duplicates atomically. 4. Retrieve the referenced payment intent from Stripe or ACP and require an authoritative successful or settled state before fulfillment. 5. Confirm that the verified merchant, currency, amount, service ID, and payment-intent ownership match locally stored order data. 6. Never trust `greenhelix_payment_id`, payer identity, amount, or service identifiers supplied solely in the webhook body. 7. Apply strict JSON schema validation and safely reject absent or malformed fields. 8. Separate checkout creation from fulfillment and use a server-generated correlation identifier. 9. Restrict the GreenHelix credential used by the webhook service to only the tools and agent records required for this integration. 10. Add rate limiting, request-size limits, structured security logging, and alerts for failed signatures and repeated event IDs. 11. Return an authentication failure before performing any API or service operation when validation fails. ]]>
