Skip to content

Fix(finstripe): resolve missing cross-vendor ownership guard in create_transfer#446

Open
Jean-Regis-M wants to merge 1 commit intoGenAI-Security-Project:mainfrom
Jean-Regis-M:patch-42
Open

Fix(finstripe): resolve missing cross-vendor ownership guard in create_transfer#446
Jean-Regis-M wants to merge 1 commit intoGenAI-Security-Project:mainfrom
Jean-Regis-M:patch-42

Conversation

@Jean-Regis-M
Copy link
Copy Markdown
Contributor

Summary

Fixes #322

Vendor portal sessions could initiate transfers to any vendor account by supplying an arbitrary vendor_id. This patch adds an ownership guard before any DB write in create_transfer, blocking cross-vendor payment fraud within the same namespace.


Problem

In finbot/mcp/servers/finstripe/server.py, the create_transfer tool accepts a caller-supplied vendor_id with no session ownership validation. A vendor portal session scoped to vendor_a can call:

create_transfer(vendor_id=vendor_b.id, amount=5000, ...)

and the transfer commits successfully enabling an attacker to drain funds to any vendor account in the same namespace.


Root Cause

session_context is available in the closure but never consulted. The call below executes unconditionally:

txn = repo.create_transaction(
    invoice_id=invoice_id,
    vendor_id=vendor_id,   # ← accepts any vendor_id, no ownership check
    ...
)

Solution

Add a 5-line early-return guard immediately before _generate_transfer_id(). The guard fires only when all three conditions hold:

if (
    getattr(session_context, "portal_type", None) == "vendor"
    and getattr(session_context, "current_vendor_id", None) is not None
    and session_context.current_vendor_id != vendor_id
):
    return {"error": "Vendor session can only initiate transfers to own account"}
  • portal_type == "vendor" scopes the restriction to vendor sessions only; admin/other sessions pass through unchanged
  • current_vendor_id is not None skips the guard when no ownership can be asserted
  • current_vendor_id != vendor_id blocks cross-vendor targeting; same-vendor transfers proceed normally
  • Error return shape {"error": str} is consistent with the existing pattern in this file

Impact

Breaking changes None
Diff size +5 lines, ±0 deleted
Regression risk Zero
New imports None

Testing

pytest tests/unit/mcp/test_finstripe.py::TestVendorSessionAccessControl::test_mcp_vendor_003_vendor_session_can_pay_different_vendor -v
pytest tests/unit/mcp/test_finstripe.py::TestVendorSessionAccessControl::test_mcp_vendor_005 -v
  • test_mcp_vendor_003 must pass: vendor session targeting a foreign vendor_id receives error response
  • test_mcp_vendor_005 must continue to pass: admin session transfer is unaffected

…sfer

Root cause:
create_transfer accepted caller-supplied vendor_id with no session ownership
validation, allowing a vendor portal session to transfer funds to any vendor.

Solution:
Add early-return guard before DB write: if session is a vendor portal session
and current_vendor_id does not match the requested vendor_id, return error dict.

Impact:
Admin and non-vendor sessions unaffected. Guard is additive with zero DB side
effects on rejection. Behavior is deterministic and stateless.

Signed-off-by: JEAN REGIS <240509606@firat.edu.tr>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant