Skip to content

Payments security hardening - #710

Closed
leewc wants to merge 4 commits into
mainfrom
payments-security-hardening
Closed

leewc wants to merge 4 commits into
mainfrom
payments-security-hardening

Conversation

@leewc

@leewc leewc commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

leewc added 4 commits October 19, 2026 20:55
The Strands plugin and LangGraph tools exposed user_id as an LLM-controllable
tool argument whose value won over the configured identity. A prompt-injected
agent could supply a different user_id to attempt an identity override. The
managed service already scopes every read to the server-resolved identity, so
no tenant boundary was crossed, but the SDK should not let the model attempt
the override in the first place.

Add resolve_authoritative_user_id() in config.py and route all 8 tool sites
(4 Strands + 4 LangGraph) through it: the configured identity is authoritative,
a differing tool-supplied value is ignored and logged. Tool signatures are
unchanged (no schema break). Adds regression tests and updates the prior
precedence tests to assert the hardened behavior.
…gGraph)

The LangGraph middleware's lenient fallback infers an x402 payment challenge
from raw tool content that lacks the trusted PAYMENT_REQUIRED: marker, so any
tool's output could mint a signed authorization to an actor-chosen recipient
and amount (CWE-863: improper authorization on an untrusted channel). The
Strands plugin is marker-only and unaffected.

Add two defense-in-depth controls on AgentCorePaymentsPluginConfig:
- allow_unmarked_402_detection (default True, preserves current behavior):
  when False, both _fallback_detect_402 call sites are skipped so only the
  trusted marker path derives a payment.
- pre_sign_policy callback at the single signing chokepoint
  (_generate_payment_header), invoked on every detection path before any
  value moves; fail-closed on a falsy return or a raise.

Adds regression tests plus an end-to-end exploit-reproduction suite that
drives the full wrap_tool_call path with an attacker-controlled unmarked
payload and asserts no authorization is minted once either control is active.
Address review findings on the pre-sign payment controls:

- Reject an async pre_sign_policy at config construction. An async policy
  cannot be awaited at the sync signing chokepoint, so the unawaited coroutine
  is truthy and the payment would sign (fail-open).
- Require the policy to return True exactly. A truthy non-bool return (a reason
  dict, a status string, a missing return) previously approved signing; now any
  value other than True blocks.
- Enforce the policy on the Strands signing path too. It lives on the shared
  config and was documented as covering both integrations, but only LangGraph
  read it, so a Strands allowlist was silently a no-op.
- Fail closed on an unresolved (None/empty) payment-required request. The
  error-retry path passed {} when extraction raised; an allowlist over an empty
  accepts list (all([]) is True) then allowed signing.
- Stop gating the post-payment rejection check behind allow_unmarked_402_detection.
  It runs on the merchant's own response after value moved; gating it let an
  unmarked rejection be reported to the model as success.

Shared is_async_callback and apply_pre_sign_policy helpers moved to
payments/_validation.py so both integrations enforce identical semantics.
Adds regression tests for each finding; documents where payTo lives across
x402 v1/v2 and MPP so a copied allowlist does not fail-close v2/MPP payments.
@leewc
leewc requested a review from a team October 23, 2026 00:03
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@leewc
leewc deployed to auto-approve October 23, 2026 00:04 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown
Contributor

✅ No Breaking Changes Detected

No public API breaking changes found in this PR.

@github-actions github-actions Bot added the size/l PR size: L label Oct 23, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Oct 23, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Oct 23, 2026
@leewc leewc closed this Oct 23, 2026
@leewc
leewc deleted the payments-security-hardening branch October 23, 2026 00:17

This branch was successfully deployed

1 active deployment
auto-approve — df42df59 Deployed Oct 23, 2026 by leewc via Test (payments) #1650
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l PR size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant