Skip to content

The safety policy hands off a Shopify order note as "moving money" while refunds pass #890

Open

Description

@mxmzb

What happens

Writing a note on a Shopify order (SHOPIFY_UPDATE_ORDER with only id and note) is handed off to the person as a money action:

approval.evaluated  composio-shopify/SHOPIFY_UPDATE_ORDER
  outcome: { behaviour: "hand_off", source: "safety",
             reason: "Paying, purchasing or moving money is always done by the person." }

Meanwhile SHOPIFY_CREATE_REFUND, which does move money, passes without a hand-off.

The payments rule in server/src/approvals/policy.ts is a keyword regex (payment|pay|checkout|purchase|place order|... |billing). The order-update action's schema or description very likely matched on a word such as billing (it has a billing_address field), while "refund" is not in the list at all.

Suggested fix

  • Classify on what the action does, not on words found anywhere in its schema. At least match against the action's name and the arguments actually sent, not field names that were left empty.
  • Add refunds to the money rule (refund, create_refund), since they move money.
  • Consider letting an administrator mark a specific action as not money-moving (an allow rule that the safety layer respects), with the decision recorded in the audit log.

Activity

  1. linear-code commented on Oct 29, 2026

    @linear-code
  2. LofiSu commented on Oct 30, 2026

    @LofiSu

    Hit a similar false-positive / false-negative pattern when gating device tools: matching against schema text (field names like billing_address) is too broad, while omitting verbs that actually move money (refund) is too narrow.

    A pattern that held up for us on a local agent node:

    1. Classify on the action id / tool name first (stable allow/deny table), then on the arguments actually sent (non-empty values only)—never on unused schema fields.
    2. Keep money-moving as an explicit denylist of action ids (*_CREATE_REFUND, *_CAPTURE_PAYMENT, …) rather than a keyword regex over the whole description/schema.
    3. Bind any allow-once approval to (action_id, args_hash, policy_version) so a later call with different args cannot reuse the token.

    That still leaves room for an admin override that marks a specific action as not money-moving, with the decision written to the audit log—exactly the third bullet in the suggested fix. Happy to dig into a concrete Shopify action pair if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions