В тестовой среде API был доступен без проблем, но сразу после передачи архива команде тестирования возникла сетевая ошибка. Бывает и обратная, более опасная ситуация: чтобы временно обойти проблему с сертификатом, кто-то оставляет NSAllowsArbitraryLoads в конфигурации выпуска. Приложение снова начинает работать, но при этом разрешаются все соединения, которые должна была блокировать ATS. Чтобы предотвратить оба сценария, нужно проверять не только Info.plist в репозитории, но и приложение, фактически собранное на облачном Mac.
Сначала определите, что проверяет контрольный барьер
Рабочий контроль ATS должен охватывать как минимум три уровня: статическую конфигурацию, TLS-рукопожатие на стороне хоста и запросы на уровне приложения. Каждый уровень отвечает на свой вопрос, поэтому один успешный вызов curl не заменяет полную проверку.
| Уровень | Объект проверки | Что проверять в первую очередь при сбое |
|---|---|---|
| Статическая конфигурация | Итоговый Info.plist внутри приложения | Конфигурацию сборки, изменения скриптами, исключения для доменов |
| Проверка TLS | Соединение от облачного Mac до целевой конечной точки | DNS, цепочку сертификатов, версию протокола, перенаправления |
| Запрос приложения | Фактическое поведение URLSession | ATS, конфигурацию сессии, аутентификацию и разбор ответа |
Сначала четко зафиксируйте правила: в выпускаемом артефакте запрещен NSAllowsArbitraryLoads; в NSExceptionDomains могут присутствовать только проверенные домены; для временных исключений обязательно указываются ответственный и условие удаления; настройки для локальной отладки не должны попадать в Release.
Исключение ATS — не универсальный переключатель, позволяющий «сначала запустить сеть», а изменение безопасности, для которого необходимо описать область действия, причину и план отмены.
Аудит итогового артефакта сборки
Исходный plist может быть переопределен через INFOPLIST_KEY_*, разные xcconfig или скрипты сборки. Сначала выполните сборку Release, а затем получите путь к готовому артефакту:
set -euo pipefail
xcodebuild \
-scheme "$SCHEME" \
-configuration Release \
-sdk iphonesimulator \
-derivedDataPath "$PWD/.derived-data" \
build
APP_PATH="$(find "$PWD/.derived-data/Build/Products" \
-type d -name '*.app' -path '*Release-*' -print -quit)"
test -n "$APP_PATH"
PLIST="$APP_PATH/Info.plist"
plutil -lint "$PLIST"
plutil -extract NSAppTransportSecurity json -o - "$PLIST" \
> "$PWD/ats-effective.json" 2>/dev/null || printf '{}
' > "$PWD/ats-effective.json"
Отсутствие словаря ATS и наличие пустого словаря должны считаться нормальными случаями, а не ошибками. Проверка должна завершаться сбоем только при обнаружении слишком широких разрешений или неподтвержденных исключений. В следующем скрипте список разрешенных доменов передается через переменную окружения, чтобы не встраивать внутренние домены команды в общедоступный скрипт:
import json
import os
import sys
with open(sys.argv[1], encoding="utf-8") as f:
ats = json.load(f)
if ats.get("NSAllowsArbitraryLoads") is True:
raise SystemExit("NSAllowsArbitraryLoads is forbidden")
approved = {
item.strip().lower()
for item in os.getenv("ATS_APPROVED_DOMAINS", "").split(",")
if item.strip()
}
exceptions = ats.get("NSExceptionDomains", {})
unknown = sorted(set(map(str.lower, exceptions)) - approved)
if unknown:
raise SystemExit("Unapproved ATS domains: " + ", ".join(unknown))
Для запуска используйте python3 ci/audit_ats.py ats-effective.json. В журнале CI можно сохранять имена ключей и результаты проверок, но не следует выводить токены запросов, Cookie или полные заголовки аутентификации.
Создайте проверяемый реестр исключений
Одного сравнения доменов недостаточно. Для каждого исключения необходимо зафиксировать разрешенные ключи ATS, среду использования, причину и условие повторной проверки. Особого внимания требует NSIncludesSubdomains: этот параметр распространяет действие исключения на все поддомены, поэтому его нельзя включать по умолчанию только потому, что сейчас используется единственный API.
Реестр рекомендуется хранить в репозитории в формате JSON или YAML, проверяя при ревью следующие пункты:
- домен должен быть указан точно, без описаний с подстановочными знаками;
- запрещено понижать версию TLS ниже базового уровня, принятого в проекте;
- запрещено разрешать весь родительский домен ради обработки одного перенаправления;
- отладочные API должны присутствовать только в конфигурации Debug;
- после удаления исключения необходимо повторно выполнить сборку и тест запросов.
Проверьте, не смешались ли конфигурации
Соберите Debug и Release по отдельности, а затем экспортируйте два файла ats-effective.json и сравните их. Если в Release появляются ключи, предназначенные только для перехвата трафика или локальных сервисов, проверка должна сразу завершиться сбоем. Не ограничивайтесь сравнением исходных файлов: даже один и тот же plist может получать разные значения из разных настроек сборки.
Проверка TLS и цепочки перенаправлений
После успешного статического аудита проверьте целевой адрес с облачного Mac, на котором выполняется сборка. Утилита nscurl выводит диагностическую матрицу ATS и помогает выявлять проблемы с протоколом, цепочкой сертификатов и прямой секретностью:
test -n "${API_URL:-}"
/usr/bin/nscurl --ats-diagnostics "$API_URL" \
> "$PWD/ats-diagnostics.txt" 2>&1
Этот вывод полезен для диагностики, но не подходит для простого правила «в файле присутствует PASS»: диагностический режим пробует несколько комбинаций ослабленных требований. Для обязательной проверки в CI следует выполнить контролируемый запрос к реальному URL проекта, ограничив время ожидания, количество перенаправлений и допустимые коды ответа:
curl --fail --silent --show-error \
--proto '=https' \
--tlsv1.2 \
--max-time 15 \
--max-redirs 3 \
--output /dev/null \
"$API_URL"
Успешный вызов curl подтверждает только доступность соединения на стороне хоста. Он не применяет конфигурацию ATS приложения iOS и не доказывает корректность прокси сессии, заголовков запросов или логики аутентификации приложения.
Добавьте регрессионный тест на уровне приложения
В завершение добавьте легковесную тестовую цель, использующую сетевой стек production. Тест должен читать URL, переданный тестовой средой, выполнять проверку состояния через URLSession и подтверждать завершение ответа, соответствие кода состояния ожидаемому значению и отсутствие перенаправления на адрес без HTTPS. Не размещайте постоянные учетные данные в тестовом коде.
Сохраняйте достаточно данных, но не больше необходимого
При сбое достаточно архивировать итоговый словарь ATS, результат сравнения со списком доменов, вывод nscurl, код состояния запроса, имя хоста назначения перенаправления и название конфигурации сборки Xcode. Текст сертификата, токены доступа и полное тело ответа обычно не нужны в долговременных журналах.
Зафиксируйте порядок выполнения: «сборка Release → аудит итогового plist → проверка TLS → тест URLSession». Такой порядок позволяет обнаруживать дрейф конфигурации и отличать отказ из-за политики приложения от изменений в цепочке сертификатов, DNS или перенаправлениях целевой конечной точки.
Контрольный список перед выпуском
Перед слиянием убедитесь, что выпускаемый артефакт не разрешает произвольные сетевые соединения, все доменные исключения присутствуют в утвержденном реестре, область действия для поддоменов явно проверена, TLS-тест использует реальный целевой адрес, а тест на уровне приложения выполняется с фактической конфигурацией URLSession. При сбое сначала сохраните артефакт и диагностические файлы и только затем изменяйте конфигурацию, чтобы не скрыть проблему сертификата или перенаправления новым исключением.
Цель такого контроля — не устранять автоматически все сетевые сбои, а локализовать их на уровне, где проблему можно исправить, и гарантировать, что временные отладочные настройки незаметно не попадут в следующий выпуск.
Часто задаваемые вопросы
Почему недостаточно проверить исходный Info.plist?
Параметры сборки и скрипты могут изменить итоговые значения. Проверять нужно Info.plist внутри собранного приложения.
Означает ли успешный nscurl, что приложение точно подключится?
Нет. Дополнительно нужен тест URLSession, учитывающий политику ATS, перенаправления и рабочий путь аутентификации.
Какие настройки ATS должны останавливать сборку?
NSAllowsArbitraryLoads, исключения доменов вне утверждённого списка и отладочные разрешения в релизном приложении.
Запустите следующую задачу разработки или сборки на облачном Mac mini
Выберите одну из двух конфигураций M4, четырех сроков аренды и пяти доступных площадок. Фактический статус доступности отображается в консоли в реальном времени.