Skip to content

Open mandate constraints: contains is dropped by codegen, so the checkout binding the schema requires is never enforced #356

Open

Description

@vishkaty

Summary

open_payment_mandate.json requires the constraints array to contain a
payment.reference, and open_checkout_mandate.json to contain a line_items.
Neither reaches the generated model or any runtime check, so an open payment
mandate with no checkout binding is created, chain verified, and constraint
checked with zero violations.

I am filing this as a question rather than a fix, because the schema rule and
another part of the specification look inconsistent, and which one is wrong is
your call. Details at the end.

The mechanical part

Walking every validation keyword in code/sdk/schemas against
code/sdk/python/ap2/sdk/generated, contains is the only enforceable keyword
in the vocabulary that does not survive generation:

keyword occurrences survives
required 35 yes, Field(...)
const 24 yes, Literal
enum 9 yes, Enum
format 7 yes, AnyUrl
pattern 2 yes, constr(pattern=)
minimum 2 yes, conint(ge=)
minItems 1 yes, min_length
exclusiveMinimum 1 yes, PositiveInt
contains 2 no

Regenerating with code/sdk/schemas/generate.py reproduces the drop byte for
byte, so this is deterministic generator behaviour rather than a stale artifact.

Reproduction

An open payment mandate with an empty constraints array, which is the shape
chain_tests.py itself constructs:

open_payment_mandate, constraints: []
  jsonschema against the shipped schema : INVALID
      "[] does not contain items matching the given schema"   (keyword: contains)
  OpenPaymentMandate.model_validate      : ACCEPTED

End to end through the SDK, a verifier that supplies the expected checkout
binding still gets nothing back, because check_payment_constraints builds an
evaluator per constraint found, so an absent constraint means
open_checkout_hash is never read.

Why the binding matters

docs/ap2/security_and_privacy_considerations.md, under Manipulated
Checkout
:

The Payment Mandate MUST contain a reference to its associated Checkout. This
is via transaction_id for closed Payment Mandates and the
mandate.payment.reference constraint for open ones.

Three things that cut against simply enforcing it

These are the reason I am not opening a pull request.

  1. Your own fixtures mostly omit it. Twelve files construct an
    OpenPaymentMandate, 47 sites in total. Only constraints_tests.py and the
    shopping_agent_v2 sample reference PaymentReference at all. Nine test
    files construct open mandates with none.
  2. payment.agent_recurrence looks incompatible with the rule as written.
    docs/ap2/payment_mandate.md:51 describes it as conditions for the agent to
    reuse one mandate multiple times, and a mandate reused many times cannot
    bind to the digest of a single checkout. The contains rule is
    unconditional.
  3. Enforcement would therefore fail a substantial part of the suite, including
    the recurrence tests, which cannot be updated without first deciding 1 and 2.

Which way would you like it

Three options as I see them, and I have no stake in which:

  • enforce contains in the SDK, and update the fixtures
  • make the rule conditional, so it does not apply when
    payment.agent_recurrence is present
  • drop contains from the schema and amend the security document

Happy to open the pull request for whichever you prefer.

Related

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions