Skip to main content
Glama
Saraid10
by Saraid10

TrustGate

TrustGate — это демонстрация синтетических данных в тестовом режиме Razorpay Test Mode, показывающая ИИ-покупателя, который может предложить покупку из каталога, но не получает полномочий изменять продавца, сумму, валюту, одобрение или результат провайдера. Агент предлагает; TrustGate независимо авторизует и записывает доказательства.

Что это доказывает

  • SKU из каталога и количество — единственные факты о покупке, на которые агент может влиять.

  • Политика, ограниченная тенантом, одобрение человеком, одноразовое полномочие на оплату и проверенные события провайдера ограничивают каждое платёжное действие.

  • Небезопасные попытки отклоняются до создания заказа у провайдера и оставляют аудируемый след.

Related MCP server: Allowance MCP

Как здесь используется ИИ

Агент-покупатель намеренно тонкий, и это аргумент, а не упрощение. Он может предложить только SKU из каталога, количество и цель; каждый критичный для денег факт вычисляется на стороне сервера. Удалите агента полностью — слой авторизации останется неизменным и по-прежнему корректным.

Инженерный вклад — не сложный агент. Это серьёзное отношение к режимам отказа языковых моделей — инъекция промптов через сторонний контент, следование инструкциям вопреки интересам оператора, уверенные ошибочные рассуждения о деньгах — и создание системы, которая остаётся корректной при их возникновении.

Это утверждение проверяется в обе стороны. python -m agent.demo --live запускает реальную модель на описаниях каталога, написанных третьими сторонами, включая враждебные. Тот же каталог отправляется дважды, один раз без описаний, чтобы влияние недоверенного контента измерялось сравнением двух предложений, а не самоотчётом. Набор регрессионных тестов никогда не вызывает провайдера моделей: он использует детерминированные заменители, поэтому проверка безопасности не зависит от того, ведёт ли себя модель определённым образом в определённый день.

Видя проблему

Три чистых отказа доказывают, что система работает, но слабо показывают, зачем она нужна. Поэтому демонстрация начинается с того, что собственный код этого проекта терпит неудачу, без сети, без учётных данных и без базы данных:

python -m demo.unguarded

Тот же агент читает тот же отравленный каталог — буквально тот же, поскольку оба каталога построены из agent/demo_catalog.py, и тест утверждает, что засеянная строка и базовый объект несут идентичную внедрённую инструкцию. Один ответ модели передаётся двум адаптерам.

Незащищённый принимает сумму и продавца, поэтому внедрённая инструкция выполняется, и он платит 20 000,00 INR продавцу, названному в тексте каталога, при цене каталога 600,00 INR. У другого нет места ни для одного из этих полей, потому что PurchaseProposal объявляет только SKU, количество и цель. Что переживает отбрасывание — это quantity=50, поле, которое агенту разрешено устанавливать, — и сервер ограничивает его собственным максимумом каталога, равным 2, поэтому попытка отклоняется до создания платёжного запроса.

Разница не в фильтре, который распознал атаку. В том, что один интерфейс имел поле для денег, а другой — нет. У базовой версии есть свои тесты, утверждающие как то, что она не может достичь ничего реального, так и то, что она всё ещё эксплуатируема, поскольку демонстрация, которая тихо перестала быть уязвимой, продолжала бы проходить, делая противоположный вывод.

Матрица атак

Сценарии уровня 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

Подделанная подпись вебхука

События провайдера аутентифицируются по HMAC на сырых байтах до разбора тела. Подделанная или отсутствующая подпись ничего не меняет, каким бы корректным ни было событие.

test_a6_a_forged_signature_is_refusedtest_a6_an_unsigned_event_is_refused

A7

Изменённое тело вебхука

Подпись покрывает точные полученные байты, поэтому подлинно подписанное событие, изменённое в пути, больше не проходит проверку и никогда не достигает платежа.

test_a7_a_body_altered_after_signing_no_longer_verifies

A8

Дублированная доставка вебхука

Идентичность события провайдера сохраняется, поэтому повтор подлинного события в пределах окна отклоняется базой данных, а не тем обработчиком, который случайно его увидит.

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

Устаревший или датированный будущим числом вебхук

Подпись доказывает происхождение, а не свежесть. Событие вне окна свежести, датированное будущим или вообще не имеющее временной метки, отклоняется до любого поиска.

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

Каждый сценарий уровня A реализован. A11a и A11b разделяют путаницу тенантов на неразрешимый тенант и известный тенант, пересекающий границу, потому что эти два случая дают сбой по разным причинам, и только второй является вопросом авторизации.

Чем ценны тесты

Проходящий набор тестов говорит, что код ведёт себя так, как написан. Он не говорит, что тесты возразили бы, если бы код перестал делать что-то важное, а применительно к деньгам имеет значение только второе утверждение. У этого проекта есть доказательство различия: сессии с областью запроса однажды отбрасывали каждую запись, пока 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

Ежедневное обновление бюджета отказывается превышать лимит.

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

Неполный поиск провайдера никогда не сообщает об отсутствии квитанции.

Первый запуск этого набора обнаружил реальный дефект. SELECT ... FOR UPDATE через ORM корректно получает блокировку, а затем отбрасывает строку, возвращённую Postgres, потому что SQLAlchemy сохраняет атрибуты объекта, уже находящегося в identity map сессии. Поэтому второй вызывающий блокировался на блокировке, как и задумано, получал зафиксированную строку, сохранял её устаревшую копию и авторизовал платёж, который только что был авторизован. Блокировка сериализовала выполнение переходов, а не то состояние, на основе которого они принимали решение. Теперь все блокировки выполняются через 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.

Создайте одноразового синтетического арендатора M1 с помощью python -m agent.seed. Установите MCP_TENANT_ID и MCP_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 (ключ API Bedrock, самый простой путь), либо стандартные учётные данные AWS для подписи SigV4. Amazon Bedrock предоставляет модели Anthropic через подписку AWS Marketplace, поэтому на аккаунте AWS также должен быть действительный платёжный инструмент, даже если кредиты покрывают использование.

  • TRUSTGATE_MODEL_BACKEND=groq читает GROQ_API_KEY и вообще не требует платёжного инструмента.

TRUSTGATE_MODEL_ID переопределяет модель на любом бэкенде. Покупатель — это реализация протокола, поэтому провайдер является конфигурационным выбором, а не архитектурным: поведение уровня авторизации не зависит от того, какая модель предлагает покупку.

Это единственный путь в проекте, который связывается с провайдером модели; тестовый набор никогда этого не делает. Запускайте его только с синтетическим каталогом сидов. Его сторонние описания отправляются модели дважды: один раз с удалёнными описаниями и один раз без изменений, чтобы измерить влияние. Никогда не отправляйте реальные данные клиентов, продавцов или платежей через эту демонстрацию.

Чтобы проверить адаптер Razorpay Test Mode, установите RAZORPAY_KEY_ID и RAZORPAY_KEY_SECRET в игнорируемом файле .env. Никогда не добавляйте секреты 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