# Карточки подтверждения VoiceOver для действий агентов

Карточка подтверждения представляет решение по безопасности, а не декоративное прерывание. Если пользователь VoiceOver не может до кнопок определить запрашивающий процесс, фактическое назначение, действие и область запроса, карточка просит согласие, не сообщая фактов, необходимых для осознанного решения.

Я видел, как команды называли запрос «доступным», потому что VoiceOver доходил до кнопки «Подтвердить». Это слишком низкая планка, и она опасна. Человек может активировать элемент управления, которого не понимает. Для действий агента последствия серьезнее: одно подтверждение может отправить запрос с сохраненными учетными данными или открыть SSH-сеанс на машине, о которой сам агент не умеет надежно рассказать.

## Карточка должна описать решение до элементов управления

Первое полезное сообщение должно обозначить решение, а все факты, нужные для него, должны прозвучать до кнопок «Подтвердить» и «Отклонить». Хорошая карточка сначала дает VoiceOver короткое резюме, а затем раскрывает детали в стабильном порядке. Пользователю не приходится разбираться в визуальной компоновке, смысл которой понятен только при наличии зрения и возможности видеть интервалы, значки и цвета.

Используйте такой порядок:

1. Почему появилась карточка: новый процесс агента запрашивает разрешение или защищенные учетные данные требуют подтверждения для этого вызова.
2. Кто запрашивает: имя процесса, при необходимости путь к исполняемому файлу и издатель подписи кода.
3. Где будет выполнено действие: протокол и конкретное назначение.
4. Что будет сделано: метод HTTP и структура запроса либо команда SSH и ее цель.
5. Что дает решение: разрешение на этот вызов, на работающий процесс до его завершения или на одно использование защищенных учетных данных.

Такая последовательность соответствует решению, которое принимает внимательный оператор. Источник запроса отвечает на вопрос, кто обращается. Назначение показывает, где проявятся последствия. Действие объясняет, что изменится. Область действия показывает, как долго сохраняется разрешение. Если поставить кнопки перед этой цепочкой, карточка превращается в проверку рефлекса.

Начальное резюме должно быть достаточно коротким, чтобы его можно было услышать за один проход. Например:

```
Authorization requested. Signed process: Acme Development, agent-helper.
HTTPS request to api.example.net. POST /v1/releases.
This approval allows this process until it exits.
```

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

В рекомендациях WAI-ARIA Authoring Practices от W3C модальный диалог описан как изолированное взаимодействие с названием, правильной обработкой фокуса и явным способом закрытия. Этот совет актуален и для приложения с нативными элементами macOS, а не только для веб-ARIA. Модальный запрос, который озвучивает лишь заголовок, выполняет самую узкую часть требования, но не помогает принять реальное решение.

## Идентификатор процесса дает сведения, но не разрешение

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

Поле идентификатора должно иметь стабильную форму, удобную для озвучивания. Сначала указывайте издателя подписи: это дает оператору понятную опору, когда несколько вспомогательных процессов имеют похожие имена файлов. Затем указывайте имя процесса. Путь добавляйте только для устранения неоднозначности, например когда локальная сборка и установленная версия имеют одно имя. Длинный путь лучше оставить в области деталей, где VoiceOver сможет прочитать его построчно.

Избегайте расплывчатых выражений вроде «доверенный агент» или зеленого значка «проверено». Такие фразы сжимают несколько разных утверждений в один успокаивающий визуальный сигнал. У процесса может быть действительная подпись, но при этом поврежденный запрос, скомпрометированный источник инструкций или обращение не к той учетной записи. Пользователю нужны сведения об источнике, а не готовый вердикт интерфейса.

Резюме запроса может выглядеть так:

```
Requesting process
Signing authority: Example Software LLC
Process: deploy-helper
Details: /Applications/Deploy Helper.app/Contents/MacOS/deploy-helper
```

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

Здесь есть еще одна ловушка: имя процесса можно изменить. Злоумышленник может назвать исполняемый файл «release-agent» и надеяться, что человек не станет читать дальше. Издателя подписи труднее подделать, но он все равно не подтверждает назначение. Карточка не должна формулировать идентификатор так, будто он гарантирует правильность запроса. Скажите «запрошено процессом» или «подписано издателем», а действие опишите отдельными предложениями.

Для неподписанных локальных процессов прямо сообщайте об этом. Не заменяйте такую формулировку эвфемизмом вроде «личная сборка». Если продукт разрешает запрос, пользователь сам решит, заслуживает ли неподписанный процесс разрешения на сеанс. Скрытие этого факта не уменьшает неудобства: оно откладывает их до момента после ошибки.

## Название назначения должно позволять его проверить

Карточка должна озвучивать конкретную конечную точку или хост, потому что категории и значки не сообщают, куда отправятся учетные данные. Для HTTP и SSH нужны разные резюме, но в обоих случаях перед элементами управления подтверждением должна звучать конкретная удаленная сторона.

Для HTTP озвучивайте протокол, хост, метод и путь, сохраняющий смысл последствий. `POST api.example.net/v1/releases` сообщает гораздо больше, чем «сервис релизов». Если порт отличается от обычного, укажите его. Если запрос следует перенаправлению, не описывайте только исходный хост, оставляя конечный хост для неприятного сюрприза. Определите назначение до запроса согласия или запросите подтверждение еще раз, если при определении изменился удаленный хост.

Для SSH озвучивайте хост или псевдоним, пользователя, если от него зависят привилегии, и команду. `ops@build-03.example.net: systemctl restart worker` дает человеку сведения для принятия решения. «Команда SSH» их не дает. Если псевдоним раскрывается через файл конфигурации, укажите разрешенный хост в основном резюме, а псевдоним оставьте в деталях. Люди часто сначала узнают знакомое имя, а уже потом фактическую машину, поэтому нужны оба значения.

К DNS применяйте такую же сдержанность. Имя хоста подходит для пользователя лучше всего. IP-адрес можно добавить, особенно для подозрительного или необычного запроса, но он не должен заменять имя хоста и превращать каждое подтверждение в проверку памяти. Если системе не удается определить цель, запрос должен прямо сообщить об этом. Не создавайте ложную уверенность с помощью старого имени из кэша.

Не полагайтесь на красную метку рабочего окружения, предупреждающий треугольник или левую колонку для различения назначений. Эти элементы помогают зрячему быстро просматривать интерфейс, но не могут нести смысл. Добавьте название окружения текстом, если у вас есть надежный источник, а затем также озвучьте хост. Названия окружений могут устаревать. Хост показывает, куда направляется соединение.

Краткое доступное представление выглядит так:

```
Destination
Protocol: HTTPS
Host: billing.example.net
Request: PATCH /v2/subscriptions/4821
Credential: billing-service token
```

Название учетных данных должно идти после назначения и действия, а не перед ними. Сначала пользователь должен понять, что сделают учетные данные, и лишь потом решить, стоит ли их использовать. Никогда не озвучивайте значение токена, даже в метке, предназначенной только для доступности. Вспомогательная технология входит в пользовательский интерфейс, а не служит закрытым каналом для секретов.

## Скрытие должно сохранять причину для подтверждения или отказа

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

Всегда скрывайте токены bearer, пароли, материалы закрытых ключей, cookie, заголовки авторизации, подписанные URL и полные значения полей, которые могут содержать персональные или финансовые данные. Маскировка только середины токена обычно создает видимость защиты. Частичный секрет все еще может помочь злоумышленнику сопоставить или восстановить учетные данные, а такой подход также подталкивает разработчиков раскрывать лишнее в журналах.

Сохраняйте структуру. Для HTTP-запроса показывайте метод, хост, путь, тип содержимого, размер тела, если он дает полезное представление о масштабе, и названия чувствительных полей. Для команды показывайте исполняемый файл, аргументы, целевые файлы или службы и явную отметку о скрытых аргументах. Пользователь, решающий, отправлять ли счет, должен услышать, что запрос содержит `recipient`, `amount` и `currency`; адрес получателя или саму сумму можно не сообщать, если политика считает их чувствительными.

Сравните два устных резюме:

```
POST request with protected details.
```

```
POST api.example.net/v1/payouts.
JSON fields: destination account redacted, amount redacted, currency visible.
This call uses the finance credential.
```

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

Не создавайте в запросе на подтверждение кнопку «показать секрет». Пользователю может понадобиться дополнительный контекст, но решение должно заключаться в более безопасном представлении, а не в простом обходе границ хранилища. Если оператору действительно нужно посмотреть защищенное значение, направьте его в отдельный специально аутентифицированный процесс с понятными правилами доступа. Карточка подтверждения должна оставаться поверхностью для решения, а не просмотрщиком секретов.

Скрытие должно сохраняться при копировании, озвучивании и отображении в аудите. Если видимая метка говорит «токен скрыт», а значение элемента доступности содержит исходный заголовок, VoiceOver становится каналом утечки. Проверяйте каждое представление поля: видимый текст, метку доступности, значение доступности, справочный текст, копирование в буфер обмена и запись в журнал.

## Дерево доступности диалога должно отражать решение

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

Используйте настоящий контейнер диалога или предупреждения с коротким доступным заголовком, например «Требуется подтверждение». При открытии перемещайте фокус внутрь диалога. Начальный фокус должен оказаться на резюме решения или первом поле идентификатора, а не на кнопке «Подтвердить». Если система сразу переводит фокус на кнопку по умолчанию, она создает короткий путь к неосознанному нажатию и заставляет пользователя возвращаться назад по диалогу, чтобы разобраться в нем.

Нативные элементы macOS уже обеспечивают большую часть поведения, которое пришлось бы воссоздавать в пользовательских представлениях. По возможности оставляйте поверхность нативной: статический текст для фактов, стандартный элемент раскрытия для необязательных деталей, флажок только тогда, когда пользователь может изменить четко объясненный параметр, и обычные кнопки с разными названиями. Тогда инспектор доступности с большей вероятностью покажет дерево, соответствующее интерфейсу.

Предполагаемый порядок должен читаться как простой план:

```
Dialog: Approval required
  Summary: New agent process requests authorization
  Group: Requesting process
    Static text: Signing authority, Example Software LLC
    Static text: Process, deploy-helper
  Group: Destination
    Static text: HTTPS, api.example.net
  Group: Requested action
    Static text: POST /v1/releases
  Group: Scope
    Static text: Approval lasts until this process exits
  Disclosure button: More redacted details, collapsed
  Button: Deny request
  Button: Approve this process
```

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

Давайте кнопкам полные названия. «Подтвердить» звучит неясно, если работу могут прерывать несколько запросов. «Подтвердить этот процесс» и «Отклонить запрос» остаются понятными, когда VoiceOver читает их отдельно. Если карточка предлагает подтверждение одного вызова и подтверждение сеанса, укажите разницу в тексте кнопки или в непосредственно предшествующем описании доступности. Две кнопки с названием «Разрешить» и разными визуальными подписями говорят о проблеме в дизайне.

В рекомендациях Apple по доступности macOS приложениям предписано предоставлять метки, роли, значения и описания, передающие назначение элемента управления. Здесь важно именно слово «назначение». Поле «Цель» может иметь метку, роль и значение, но все равно заставлять пользователя догадываться, обозначает ли оно хост, учетную запись, файл или команду. Используйте метки с недостающим существительным: «Хост SSH», «HTTP-запрос», «Используемые учетные данные» и «Срок подтверждения».

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

## Цвет и компоновка не могут передавать предупреждение

Карточка подтверждения проходит базовую проверку доступности только тогда, когда человек может принять то же решение с помощью озвученного порядка и клавиатуры, что и при визуальном просмотре. Такая проверка выявляет не только проблемы с контрастом. Она обнаруживает скрытые предположения о колонках, расположении значков, начальном фокусе и визуальном объединении элементов.

Проведите это упражнение с тестировщиком, который регулярно пользуется VoiceOver, а не только с разработчиком, знающим расположение каждого элемента. Запустите новый процесс агента. Включите VoiceOver. Уберите указатель от элементов управления. Попросите тестировщика ответить на четыре вопроса до подтверждения или отклонения: кто сделал запрос, где будет выполнено действие, что произойдет и сколько продлится подтверждение.

Затем попросите его выполнить такую последовательность:

1. Найти входящий запрос на подтверждение без помощи зрячего человека.
2. По порядку прочитать идентификатор процесса, назначение, действие и область действия.
3. Раскрыть детали и определить, какие значения интерфейс скрыл.
4. Отклонить запрос, затем вызвать новый запрос и подтвердить его.
5. После каждого выбора проверить, что произошло и где найти записанное действие.

Записывайте фактическую последовательность озвучивания, включая повторяющиеся метки, пропущенные поля и скачки фокуса. При визуальном просмотре можно не заметить неправильный порядок доступности, потому что проверяющий видит задуманную компоновку и неосознанно восполняет пробелы. Аудиозаписи и письменные расшифровки делают дефект очевидным.

Проверяйте эти случаи отдельно:

- Подписанный процесс агента запрашивает первое подтверждение сеанса.
- Неподписанный локальный процесс делает такой же запрос.
- Защищенные учетные данные требуют подтверждения вызова после того, как подтверждение сеанса уже выдано.
- Запрос содержит скрытые заголовки или поля тела.
- Два запроса приходят почти одновременно, а первая карточка исчезает после отклонения.

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

Также проверяйте настройки уменьшения движения и крупный текст. Они напрямую не меняют семантику VoiceOver, но часто запускают альтернативный код компоновки. Если компактный вариант перемещает область действия под кнопки или удаляет метку ради экономии места, пользователь теряет информацию именно тогда, когда его настройкам особенно нужен более снисходительный интерфейс.

## Подтверждение сеанса должно иметь видимую границу

Разрешение сеанса безопасно только тогда, когда человек точно понимает, какой работающий процесс его получил и когда разрешение закончится. Формулировка «Запомнить мой выбор» не подходит агенту: она звучит как постоянная настройка, хотя решение должно быть привязано к сроку жизни одного процесса.

Укажите границу прямо в карточке: «Это подтверждение разрешает вызовы от этого процесса до его завершения». Разместите фразу после описания действия, где она отвечает на естественный следующий вопрос. Если процесс завершился и запущена его замена, снова покажите карточку. У нового процесса может быть такое же отображаемое имя, но старое решение на него не распространяется.

Sallyport по умолчанию использует авторизацию на сеанс и начинает карточку подтверждения с издателя подписи кода процесса. Это полезная отправная точка, но карточка все равно должна столь же понятно сообщать человеку, использующему VoiceOver, назначение, действие и срок.

Подтверждение каждого вызова выполняет другую задачу. Используйте его для отдельных учетных данных, когда каждое применение имеет самостоятельные последствия, например для платежа, удаления в рабочем окружении или команды SSH, меняющей состояние службы. Подтверждение должно прямо сообщать: «Для каждого использования этих учетных данных требуется подтверждение». Не прячьте условие в экране настроек, чтобы потом удивлять пользователя дополнительным запросом.

Не следуйте популярной рекомендации спрятать все решения за одним широким переключателем «разрешить этому агенту». Он нравится людям, потому что автономная работа продолжается без остановок. Но он объединяет идентификатор, цель и срок в обещание, что агент будет вести себя правильно. У этого обещания нет полезного смысла для безопасности, когда агент получает другую инструкцию или обращается к другой службе. Ограниченное разрешение сеанса и выборочное подтверждение каждого вызова предлагают человеку оценивать конкретный запрос.

Ограничение хранилища должно находиться за пределами этого выбора. Когда хранилище заблокировано, система должна сразу отклонять действия, а не показывать карточку, создающую впечатление, будто нажатие может обойти блокировку. Если продукт поддерживает Touch ID, это действие может разблокировать или подтвердить операцию, но голосовой интерфейс должен сообщать, какое именно событие произошло. «Подтверждено» и «хранилище разблокировано» обозначают разные изменения состояния и не должны объединяться в одно неоднозначное сообщение.

## Записи аудита должны позволять восстановить решение позже

Процесс подтверждения неполон, если оператор не может позже определить, какой запрос он подтвердил, какое действие последовало и не изменялась ли запись. Карточка обрабатывает согласие в моменте. Журнал аудита помогает разобраться позже, когда никто уже не помнит, что именно мелькнуло на экране.

Разделяйте событие сеанса и событие отдельного действия. Запись сеанса должна содержать идентификатор запуска агента, принятое решение об авторизации и отзыв разрешения или завершение процесса. Запись активности должна содержать каждый вызов HTTP или SSH, его назначение, операцию, результат и те же правила скрытия, что применяются в карточке подтверждения. Если объединить все в одну расплывчатую запись «агент разрешен», связь между согласием и последствием потеряется.

Для зашифрованного журнала с цепочкой хешей проверка должна быть отдельным действием, результаты которого можно изучить. Ожидаемый вид прост:

```
$ sp audit verify
Verifying encrypted audit log...
Chain verified: 184 records
Result: valid
```

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

Формируйте карточку подтверждения и журналы из одного словаря событий. Если карточка говорит «HTTPS-запрос к billing.example.net», а журнал называет его «удаленная операция 12», пользователь не сможет сопоставить решение с записью. Повторно используйте назначение, действие, идентификатор процесса, область действия и категории скрытия, даже если на разных поверхностях они представлены с разной степенью подробности.

Дайте пользователям VoiceOver такой же прямой путь к записи. После подтверждения или отклонения сообщите результат и предложите именованный переход к соответствующей записи сеанса или активности. Не заставляйте цветную точку состояния доказывать, что действие произошло. Пользователь, который услышал «Запрос отклонен. Запись активности доступна», сможет проверить результат позже, не восстанавливая интерфейс по памяти.

## Регрессии доступности должны входить в набор тестов безопасности

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

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

Практическая проверка задает четыре прямых вопроса. Может ли пользователь VoiceOver определить исполняемый файл и издателя подписи? Может ли он определить конечное удаленное назначение? Понимает ли он разницу между этим вызовом и сроком жизни процесса? Может ли он подтвердить или отклонить запрос и затем услышать понятный результат? Если любой ответ зависит от цвета, интервалов или значка, в запросе по-прежнему есть дефект безопасности.

Первую проверку проводите на самых важных учетных данных и самом неудобном запросе, который допускает продукт. Безобидный GET к знакомому хосту делает любую карточку подтверждения удачной. Дизайн по-настоящему проверяет незнакомый подписанный процесс, который просит изменить реальную службу, причем часть его тела скрыта. Именно здесь формулировки, порядок и поведение фокуса либо позволяют человеку контролировать решение, либо сводят его роль к нажатию кнопки на ограничителе.
