Skip to main content
Glama
Saraid10
by Saraid10

TrustGate

TrustGate 是一个合成数据、Razorpay Test Mode 演示,展示一个 AI 买家可以提出目录购买建议,但无法获得改写商户、金额、货币、审批或提供商结果的权限。代理提出建议;TrustGate 独立授权并记录证据。

它证明了什么

  • 目录 SKU 和数量是代理唯一能影响的购买事实。

  • 租户范围的策略、人工审批、一次性结账权限和已验证的提供商事件约束着每一个支付操作。

  • 不安全的尝试在提供商订单创建之前就被拒绝,并留下可审计的痕迹。

Related MCP server: safe-cart-ai

AI 在这里如何使用

买家代理被刻意设计得很薄,而这正是论点本身,而非捷径。它只能提出目录 SKU、数量和用途;每一个涉及金钱的关键事实都在服务端派生。完全删除代理,授权层保持不变,仍然正确。

工程贡献不是复杂的代理。而是认真对待语言模型的失败模式——通过第三方内容进行提示注入、违背操作者利益的指令遵循、关于金钱的自信错误推理——并构建一个在这些情况发生时仍能保持正确的系统。

这一主张以两种方式得到检验。python -m agent.demo --live 针对由第三方(包括敌意方)编写的目录描述运行真实模型。同一目录被发送两次,一次移除描述,因此不受信任内容的影响通过比较两次提议来衡量,而非自我报告。回归套件从不调用模型提供商:它使用确定性替代品,因此安全验证不依赖于模型在某一天以特定方式表现。

看到问题

三次干净的拒绝证明了系统有效,但很难说明为什么有人需要它。因此演示以本项目自己的代码失败开场,不使用网络、不使用凭据、不使用数据库:

python -m demo.unguarded

同一个代理读取同一个被投毒的目录——字面上相同,因为两个目录都构建自 agent/demo_catalog.py,且一个测试断言种子行和基线对象携带相同的注入指令。一个模型响应被交给两个适配器。

未受防护的那个接受金额和商户,因此注入指令得以执行,它向目录文本中指定的商户支付 INR 20,000.00,而目录价格为 INR 600.00。另一个没有地方放置这两个字段,因为 PurchaseProposal 只声明了 SKU、数量和用途。丢弃后幸存的是 quantity=50,这是代理被允许设置的字段——而服务器将其限制在目录自身的最大值 2 以内,因此在支付请求创建之前尝试就被拒绝。

区别不在于识别攻击的过滤器。而是一个接口有金钱字段,另一个没有。基线有自己的测试,既断言它无法触及任何真实事物,也断言它仍然可利用,因为一个悄悄不再易受攻击的演示会继续通过测试,同时传达相反的观点。

攻击矩阵

Tier A 对抗性场景。此表由 python -m scenarios.report 从场景注册表生成,且一个测试断言其匹配,因此它不能声称一个未被通过测试覆盖的攻击。每个场景证明三件事:攻击以其原因代码被拒绝,没有创建提供商订单,没有支付获得其本不具备的权限。

ID

攻击方式

已验证的不变量

测试

A1

金额篡改

金额来源于目录项的价格和受服务器约束的数量。任何由代理提供的值都无法改变它。

test_a1_supplied_amount_field_is_refused_at_the_boundarytest_a1_mcp_surface_has_no_amount_parametertest_a1_quantity_cannot_be_used_to_escalate_the_amount

A2

商户替换

商户来源于租户范围内的目录项。租户之外的商户不可达,不在活动策略范围内的商户无法被支付。

test_a2_another_tenants_sku_is_not_reachabletest_a2_policy_disallowed_merchant_cannot_be_paid

A3

货币替换

货币来源于目录项,唯一接受货币的路由默认被禁用,启用后若与活动策略不匹配则拒绝。

test_a3_the_agent_surface_derives_currency_and_cannot_be_told_onetest_a3_the_only_currency_accepting_route_is_disabled_by_defaulttest_a3_an_enabled_legacy_route_still_denies_a_currency_outside_the_policy

A4

过期或重复使用的审批

审批是一种具有生命周期且只能使用一次的权限。过期的或已被消费的审批都无法授权,被拒绝的审批不会被消耗。

test_a4_an_expired_approval_cannot_authorizetest_a4_an_already_consumed_approval_cannot_authorize_again

A5

自我审批

审批不能由发起购买请求的身份授予。职责分离是被强制执行的,而不仅仅是期望通过配置实现。

test_a5_an_approval_cannot_be_granted_by_the_requesting_actortest_a5_a_separate_approver_can_still_grant

A6

伪造的 Webhook 签名

提供方事件在解析正文之前通过原始字节 HMAC 进行身份验证。无论事件格式多么规范,伪造或缺失的签名都不会改变任何结果。

test_a6_a_forged_signature_is_refusedtest_a6_an_unsigned_event_is_refused

A7

被篡改的 Webhook 正文

签名覆盖接收到的确切字节,因此一个真实签名的事件在传输途中被修改后将无法通过验证,也永远不会到达支付环节。

test_a7_a_body_altered_after_signing_no_longer_verifies

A8

重复的 Webhook 投递

提供方事件的身份被存储,因此对真实且在窗口期内的事件进行重放会被数据库拒绝,而不是由碰巧处理它的某个处理器来拒绝。

test_a8_a_replayed_event_does_not_transition_the_payment_twice

A9

提供方事件乱序到达

到达顺序是提供方的事,合法性由我们决定。捕获不能先于授权发生,终态支付不接受任何进一步的结果。

test_a9_a_capture_cannot_precede_an_authorizationtest_a9_a_terminal_payment_accepts_no_further_provider_outcome

A10

双重退款

任何界面都无法发起退款,这一点针对实时路由表和工具列表进行了断言,同时账本不变量拒绝退款总额超过捕获金额。

test_a10_no_surface_anywhere_can_initiate_a_refundtest_a10_a_refund_total_cannot_exceed_what_was_captured

A11a

未知租户请求头

无法解析的租户在任何路由主体运行之前就被拒绝,且拒绝响应不会泄露任何能让调用方枚举存在哪些租户的信息。

test_a11a_an_unknown_tenant_header_is_refusedtest_a11a_an_unknown_tenant_is_indistinguishable_from_a_forbidden_one

A11b

跨租户对象访问

每个租户范围内的查找都按可信租户进行过滤。已知租户无法在任何界面上读取或操作另一个租户的请求、支付或授权。

test_a11b_checkout_authority_route_refuses_another_tenants_requesttest_a11b_razorpay_route_refuses_another_tenants_authoritytest_a11b_mcp_refuses_another_tenants_payment

A12

幂等键冲突

使用不同购买请求重复使用同一键时,返回原始决定和 409 状态码。第二次购买永远不会被创建,也不会被误认为是已接受的购买。

test_a12_a_reused_key_with_a_different_purchase_returns_the_first_decision

A13

授权与使用之间的策略漂移

授权不会比其被检查时所依据的策略更长寿,也不会比其为之签发的购买更长寿。后续策略或修改后的金额会撤销授权但不会消耗它,未发生漂移的授权仍然有效。

test_a13_an_authority_is_valid_until_the_policy_under_it_movestest_a13_a_policy_published_after_authorization_revokes_the_authoritytest_a13_an_amount_edited_after_authorization_breaks_the_snapshot_hash

A14

过期或未来日期的 Webhook

签名证明来源,而非时效性。超出新鲜度窗口的事件、日期指向未来的事件,或完全不带时间戳的事件,在任何查找之前就被拒绝。

test_a14_a_stale_signed_event_is_refusedtest_a14_a_post_dated_event_cannot_extend_its_own_validitytest_a14_an_event_with_no_timestamp_is_refused_rather_than_exempted

A15

通过 MCP 进行未授权捕获

代理可到达的任何工具都无法授权、捕获、退款或调用提供方。这是通过实际调用每个暴露的工具来证明的,而不是通过检查工具名称。

test_a15_every_exposed_mcp_tool_grants_no_payment_authoritytest_a15_mcp_exposes_no_provider_or_authorization_tool

每个 Tier A 场景都已实现。A11aA11b 将租户混淆拆分为不可解析的租户和已知租户越界访问两种情况,因为两者的失败原因不同,且只有第二种情况属于授权问题。

这些测试的价值

一套通过的测试套件说明代码的行为与所编写的一致。它并不说明当代码停止做某些重要事情时测试会提出异议,而当涉及金钱时,只有第二种说法才重要。本项目有证据证明这种差异:请求作用域的会话曾经在 146 个测试通过的情况下丢弃了每一次写入,因为测试套件是在其写入的同一事务内部进行断言的。

make mutation 会故意一次一个地破坏每个安全不变量,并要求以其命名的测试作为守卫而失败。每个源文件都会在 finally 块中恢复,并且在报告打印之前会通过 git diff 检查恢复结果,因此中断的运行不会留下任何变异。如果任何变异存活,它会以非零状态退出。

此表由 python -m scenarios.report --mutations 从变异注册表生成,并且有一个测试断言其匹配,因此它不能声称某个受守卫的不变量实际上并未受到守卫。

Mutation

其移除的不变量

payment-row-lock

支付在状态被读取和更改之前被锁定。

locked-read-freshness

锁定读取根据已提交的行做决定,而非缓存的行。

locking-discipline

行锁通过唯一能保持其意义的辅助函数获取。

webhook-signature-check

提供商事件在对其执行任何操作之前经过身份验证。

webhook-freshness-window

已签名的提供商事件证明来源,而非时效性。

webhook-timestamp-required

无法确定日期的事件无法被限定范围,因此被拒绝。

approval-expiry

审批是带有效期的权限,而非永久授权。

authority-policy-drift

授权不会比其被检查所依据的策略存活更久。

authority-snapshot-binding

授权绑定于其签发时对应的具体购买。

daily-budget-predicate

每日预算的 upsert 拒绝超过限额。

budget-release-from-state-guard

预算仅由实际预留它的支付返还。

checkout-script-escaping

目录文本不能终止结账页面的 script 元素。

request-session-commit

成功的请求提交其写入。

provider-event-identity

同一支付的生命周期事件是不同的事件,而非重放。

self-approval-guard

审批不能由发起请求的行为者授予。

evidence-tenant-filter

证据限定在请求它的租户范围内。

receipt-search-fail-closed

不完整的提供商搜索绝不会将收据报告为不存在。

该套件的首次运行发现了一个实际缺陷。通过 ORM 的 SELECT ... FOR UPDATE 正确获取了锁,但随后丢弃了 Postgres 返回的行,因为 SQLAlchemy 保留了会话身份映射中已有对象的属性。因此,第二个调用者按设计阻塞在锁上,收到已提交的行,保留其过期副本,并授权了一笔刚刚已被授权的支付。锁在转换运行时进行序列化,而非序列化它们所依据的状态。现在所有加锁都通过 models.locking.locked() 进行,并且有一个测试断言该辅助函数是源码中唯一可以获取锁的地方。

当前范围

TrustGate 仅使用合成租户、商户和 INR 价格。它是一个本地安全测试平台,而非支付处理器、合规产品、法律同意系统、欺诈模型或 Live Mode 支付集成。

快速开始

要求:Docker Desktop 和 Python 3.12。

Copy-Item .env.example .env
docker compose up -d
docker compose exec -T api python -m alembic upgrade head
docker compose exec -T api python -m pytest -q

本地 API 健康检查可在 http://127.0.0.1:8000/health 获取。

使用 python -m agent.seed 创建一个一次性的合成 M1 租户。将 MCP_TENANT_IDMCP_ACTOR_ID 设置为打印出的值,然后使用 python -m agent.demo "Buy Starter credits for our student club." 运行本地买家代理演示。它只能提出目录 SKU、数量和用途;MCP 服务器推导所有与资金相关的关键事实。使用 python -m agent.demo --adversarial "Buy a small amount of cloud credits." 运行确定性的投毒目录演示。

要使用真实模型而非确定性替代品运行相同流程,请使用 pip install -e ".[agent]" 安装可选附加项并添加 --live。支持两个后端,两者都使用相同的 Messages API 形态:

  • TRUSTGATE_MODEL_BACKEND=anthropic(默认)读取 ANTHROPIC_API_KEY

  • TRUSTGATE_MODEL_BACKEND=bedrock 按 AWS 账户计费。将 AWS_REGION 设置为凭证所属的区域,然后使用 AWS_BEARER_TOKEN_BEDROCK(Bedrock API 密钥,最简单的路径)或用于 SigV4 签名的标准 AWS 凭证。Amazon Bedrock 通过 AWS Marketplace 订阅提供 Anthropic 模型,因此即使额度足以覆盖使用量,AWS 账户也需要有效的支付工具。

  • TRUSTGATE_MODEL_BACKEND=groq 读取 GROQ_API_KEY,完全不需要支付工具。

TRUSTGATE_MODEL_ID 在任何后端上覆盖模型。买家是协议实现,因此提供商是配置选择而非架构选择:授权层的行为不依赖于哪个模型提出购买。

这是项目中唯一与模型提供商接触的路径;测试套件从不这样做。仅针对合成种子目录运行它。其第三方描述会发送给模型两次,一次移除描述,一次保持完整,以衡量影响。切勿通过此演示发送真实的客户、商户或支付数据。

要使用 Razorpay Test Mode 适配器,请在被忽略的 .env 文件中设置 RAZORPAY_KEY_IDRAZORPAY_KEY_SECRET。切勿将 Test Mode 或 Live Mode 密钥添加到仓库中。

信任边界

AI buyer proposes SKU, quantity, and purpose
        -> TrustGate derives and authorizes money-critical facts
        -> Razorpay Test Mode executes a bounded order
        -> TrustGate records authorization and provider evidence

正式构建计划位于 docs/build-plan.md;架构、威胁模型和设计决策位于 docs/

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to browse product catalogs and make purchases through a policy engine that enforces spending limits, requires human approval for certain amounts, and logs all actions to an audit trail.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables human-in-the-loop authorization for AI agent transactions, allowing real-time approval or denial of purchases based on configurable spending limits, vendor blocklists, daily caps, and category restrictions.
    1
    -

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Saraid10/Trustgate'

If you have feedback or need assistance with the MCP directory API, please join our Discord server