Чтение заказа начинается с ownership check
Даже read-only tool раскрывает персональные данные, поэтому чтение заказа начинается не с запроса, а с проверки владения. Запрос к базе всегда включает actor и tenant из доверенной сессии, а ответ минимизируется до полей, которые агенту действительно нужны. Модель называет order_id; всё остальное решает сервер.
async function findPublicOrder(
orderId: string,
context: TrustedToolContext
) {
const row = await db.order.findFirst({
where: {
id: orderId,
customerId: context.actorId,
tenantId: context.tenantId
},
select: {
id: true,
publicStatus: true,
deliveredAt: true,
currency: true,
refundableSubtotal: true,
version: true
}
});
return row ? { found: true, order: row } : { found: false };
}Две детали здесь спасают от утечек. Первая: несуществующий и чужой order id возвращают ОДИНАКОВЫЙ публичный результат { found: false } - иначе tool превращается в oracle для перебора чужих заказов. Вторая: даже если пользователь назвал email или телефон в чате, сопоставление identity выполняет ваш проверенный auth flow, а не модель по тексту.
Rate limit тоже должен учитывать actor и tenant, а не только IP.
- Ограничьте число разных order ids за короткий период.
- Записывайте denied lookups без чувствительного payload.
- Не возвращайте внутренний fraud status или комментарии оператора.
- Не позволяйте модели строить произвольные SQL, GraphQL или фильтры.
Заказ прочитан безопасно. Самое рискованное действие - возврат денег - мы намеренно не даём агенту целиком, а делим на три шага.