Перезапись тела
Chute может искать и заменять содержимое в телах HTTP-запросов и ответов, используя регулярные выражения или выражения JSONPath. Для HTTPS-трафика требуется расшифровка MitM.
Внимание: Обычный HTTP-запрос обрабатывается, только если он приходит в Chute через его HTTP-прокси; обычный HTTP, пришедший через интерфейс TUN, пересылается без изменений. Chute Android направляет весь трафик через TUN, если в настройках не включён Системный HTTP-прокси, который предоставляет приложениям HTTP-прокси Chute (Android 10 и новее; по умолчанию выключен).
Поддерживаются и jq-программы Surge http-request-jq и http-response-jq — см. Синтаксис Surge.
Правила перезаписи тела определяются в разделе [Body Rewrite]. Для каждого направления (запрос / ответ) к сообщению применяется только первое совпадающее правило. Если с тем же сообщением совпадает и скрипт http-request или http-response, получающий тело, сначала применяется правило, и скрипт видит уже перезаписанное тело; скрипт http-request-before-send выполняется после них обоих.
[Body Rewrite]
^https://api\.example\.com/response.* response regex "old-text" "new-text"
^https://api\.example\.com/request.* request regex "sensitive" "[redacted]"
^https://api\.example\.com/data.* jsonpath-response jsonpath $.ads null
Формат правила
Каждое правило следует этому общему формату:
<URL regex> [direction] <mode> <pattern> <replacement>
Внимание: URL Regex сопоставляется с любым местом полного URL запроса, а также одного лишь его пути, как в перезаписи URL:
^https://example\.comуже охватывает все пути и параметры запроса этого хоста, поэтому.*в конце не нужен, а^/apiсовпадает с каждым запросом, путь которого начинается с/api. Полный URL расшифрованного запроса начинается сhttps://, поэтому шаблон^http://с таким запросом никогда не совпадает. Шаблон сохраняет регистр, так что\S,\D,\Wи\Bзначат то, что написано; само сопоставление регистр не учитывает.
# или // в начале строки или после пробела открывает комментарий до конца строки, если только он не стоит внутри двойных кавычек; ; — никогда. См. Комментарии.
Синтаксис Surge
Принимается и собственная форма строки Surge: направление пишется первым, а ключевое слово regex опускается.
[Body Rewrite]
http-response ^https://api\.example\.com/feed "\"ads\":\s*\[.*?\]" "\"ads\":[]"
http-request ^https://api\.example\.com/submit "sensitive" "[redacted]"
Строка Surge может нести несколько пар «шаблон/замена»; они применяются слева направо, каждая к результату предыдущей. http-request-jq и http-response-jq принимают программу jq вместо шаблона и замены: <тип> <шаблон URL> <программа jq>; программу обычно берут в кавычки, так как в ней есть пробелы. Вне кавычек // или # после пробела начинает комментарий, на котором программа заканчивается; внутри них // — это оператор альтернативы jq, поэтому программу, которая его использует, берите в кавычки. Она применяется к телу как к JSON. Тело, не являющееся JSON, программа, выбросившая ошибку, и программа без вывода — все три случая оставляют тело без изменений; недействительная программа сообщается в журнале, и пропускается только это правило, а остальной профиль продолжает работать. Программа не может читать ни файлы, ни окружение: import и include ничего не находят, а $ENV и env пусты.
Внимание: Chute Android выполняет программы jq на jackson-jq. Его вывод ограничен 4096 результатами или 4 МБ — программа, выдающая больше, оставляет тело без изменений, — и в нём нет некоторых встроенных функций jq 1.7, среди них функции работы с датами, кроме
now,todateiso8601иfromdateiso8601(strftime,strptime,mktime,gmtime,todateи подобные),@base32и@base32d,abs,toarray,trim,ltrimиrtrim,IN,INDEXиJOIN,tostreamиfromstream. Программа, вызывающая одну из них, выбрасывает ошибку при выполнении, и тело остаётся без изменений.
Направление
| Ключевое слово | Описание |
|---|---|
response |
Применить к телу ответа (по умолчанию, если не указано) |
request |
Применить к телу запроса |
Режимы
| Режим | Описание |
|---|---|
regex |
Поиск и замена с использованием регулярного выражения |
jsonpath-response / jsonpath-request / body-jsonpath-response / body-jsonpath-request |
Модификация на основе JSONPath |
Режим Regex
Выполняет стандартный поиск и замену с использованием регулярного выражения в декодированном тексте тела. Использует NSRegularExpression (ICU) с сопоставлением без учёта регистра. Замена поддерживает ссылки на группы захвата ($1, $2 и т.д.).
<URL regex> [response|request] regex <pattern> <replacement>
Пример — удаление рекламы из JSON-ответа:
[Body Rewrite]
^https://api\.example\.com/feed.* response regex "\"ads\":\s*\[.*?\]" "\"ads\":[]"
Пример — очистка тела запроса:
[Body Rewrite]
^https://api\.example\.com/submit.* request regex "\"password\":\s*\".*?\"" "\"password\":\"[FILTERED]\""
Пример — использование групп захвата для переформатирования данных:
[Body Rewrite]
// Замена "last, first" на "first last"
^https://api\.example\.com/users.* response regex "\"name\":\s*\"(\w+),\s*(\w+)\"" "\"name\":\"$2 $1\""
Пример — перезапись встроенных URL в теле ответа:
[Body Rewrite]
^https://api\.example\.com.* response regex "https://old-cdn\.example\.com" "https://new-cdn.example.com"
Токены, содержащие пробелы, должны быть заключены в двойные кавычки:
[Body Rewrite]
^https://example\.com.* response regex "old value with spaces" "new value"
Чтобы включить буквальную двойную кавычку внутри заключённого в кавычки токена, экранируйте её обратной косой чертой: \".
Режим JSONPath
Модифицирует тела JSON с использованием выражений JSONPath. Поддерживает чтение, установку и удаление значений по указанным путям.
<URL regex> jsonpath-response|jsonpath-request jsonpath <jsonpath-expression> [value]
Поддерживаемый синтаксис JSONPath
| Выражение | Описание |
|---|---|
$.key |
Доступ к свойству объекта |
$.key.subkey |
Доступ к вложенным свойствам |
$[0] |
Доступ к элементу массива по индексу |
$.key[0].subkey |
Смешанный доступ к объекту и массиву |
$.items[*].name |
Подстановочный знак: все элементы в массиве |
$.*.value |
Подстановочный знак: все свойства |
.* берёт каждое свойство объекта и каждый элемент массива, поэтому $.items.*.name доходит до имени каждого элемента. Как последний шаг, применённый к массиву, он ничего не меняет; чтобы заменить сами элементы, используйте [*].
Типы значений
| Значение | Результат |
|---|---|
string |
Установить строковое значение — всё, что не подходит ни под одну из форм ниже, поэтому example.com, 1.0.0-beta и 12abc остаются строками |
42 |
Установить целое число — токен, который целиком является числом JSON без дробной части и экспоненты |
3.14 |
Установить число с плавающей запятой — число JSON с дробной частью или экспонентой, например 1e3, или целое, не помещающееся в 64 бита |
true |
Установить булево значение true |
false |
Установить булево значение false |
[…] или {…} |
Без кавычек разбирается как JSON и устанавливается этим массивом или объектом; это последнее поле, поэтому остаток строки берётся как написан и может содержать пробелы. В кавычках или если это не корректный JSON — остаётся строкой. |
null, nil или опущено |
Удалить путь |
Внимание: Заключение значения в кавычки не приводит принудительно к строковому типу — кавычки удаляются при токенизации, и тип выводится из оставшегося содержимого, поэтому
"42"становится числом 42,"true"— булевым значением true, а"null"удаляет путь; кавычки имеют значение только для[…]и{…}, которые в кавычках остаются строками. Ключевые слова сравниваются точно, с учётом регистра, так чтоTrueиNULL— строки. Числом считается только токен, который целиком является числом в записи JSON:+1,.5,1.и007остаются строками, как и число, слишком большое даже для числа с плавающей запятой, например1e400. Написания, которое установило бы строку вроде"42"или"true", нет — используйте для этого правилоregex.
Пример — установка поля JSON:
[Body Rewrite]
^https://api\.example\.com/profile.* jsonpath-response jsonpath $.user.name "Anonymous"
Пример — удаление поля JSON:
[Body Rewrite]
^https://api\.example\.com/data.* jsonpath-response jsonpath $.tracking null
Пример — модификация с подстановочным знаком:
[Body Rewrite]
^https://api\.example\.com/list.* jsonpath-response jsonpath $.items[*].hidden true
Практические примеры
Удаление параметров отслеживания из JSON-ответов
Удаление поля trackingId из всех API-ответов. Поскольку для каждого направления применяется только первое совпадающее правило, используйте одно правило на каждый шаблон URL:
[Body Rewrite]
^https://api\.example\.com/.* jsonpath-response jsonpath $.trackingId null
Внедрение тега script в HTML-ответы
Добавление пользовательского тега <script> перед </body> на всех HTML-страницах:
[Body Rewrite]
^https://www\.example\.com/.* response regex "</body>" "<script>console.log('injected')</script></body>"
Скрытие конфиденциальных полей в журналах запросов
Замена ключей API и токенов в исходящих телах запросов до их достижения сервера. Поскольку для каждого направления применяется только первое совпадающее правило, объедините оба поля в одно правило:
[Body Rewrite]
^https://api\.example\.com/.* request regex "\"(apiKey|token)\":\s*\"[^\"]+\"" "\"$1\":\"[REDACTED]\""
Нормализация форматов дат в ответах
Замена дат ISO на более короткий формат:
[Body Rewrite]
^https://api\.example\.com/.* response regex "(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z" "$1/$2/$3 $4:$5"
Отключение флагов функций в конфигурации приложения
Принудительная установка всех флагов функций в false в конечной точке конфигурации:
[Body Rewrite]
^https://api\.example\.com/config.* jsonpath-response jsonpath $.features[*].enabled false
Перезапись URL CDN в кэшированных ответах
Замена всех ссылок на старый CDN новым:
[Body Rewrite]
^https://www\.example\.com/.* response regex "https://old-cdn\.example\.com" "https://new-cdn.example.com"
Конвейер обработки
Перезапись тела автоматически обрабатывает:
- Content-Encoding: Поддерживает
gzipиdeflate. Пропускает неподдерживаемые кодировки. - Transfer-Encoding: Собирает тело из фрагментов (chunked transfer encoding) перед обработкой.
- Декодирование: Распаковывает тела перед применением правил перезаписи.
- Перекодирование: Пересжимает тела и обновляет
Content-Length. Удаляет заголовокTransfer-Encoding. - Accept-Encoding: Когда правило для ответа совпадает с URL запроса, заголовок
Accept-Encodingзапроса перезаписывается вgzip, deflate, identity, чтобы ответ оставался в декодируемой кодировке.
Максимальный размер тела для обработки перезаписи на HTTP/1.x (обычный HTTP и расшифрованный HTTP/1.1) составляет 128 КБ; тела большего размера проходят без модификации. Расшифрованное сообщение HTTP/2 буферизуется целиком и перезаписывается независимо от размера.
На HTTP/1.x тело ответа перезаписывается, когда оно пришло целиком. Если сервер закрыл соединение раньше, тело без
Content-Lengthи без chunked-кодирования завершено — закрытие и есть его конец, — и оно перезаписывается как обычно. Тело сContent-Lengthили chunked-кодированием, которое закрытие оборвало, уходит клиенту ровно в том виде, в каком пришло, без перезаписи и с нетронутымContent-Length, после чего соединение закрывается, чтобы клиент мог понять, что ответ неполный.
Примечания
- Для HTTPS-трафика должна быть включена расшифровка MitM для соответствующего имени хоста.
- Сопоставление регулярных выражений нечувствительно к регистру. Шаблон замены поддерживает ссылки на группы захвата ICU:
$0(полное совпадение),$1(первая группа),$2(вторая группа) и т.д. - Шаблон URL или шаблон для тела, не являющийся допустимым регулярным выражением, делает строку regex или JSONPath ошибкой конфигурации, и правило не загружается; строка jq вместо этого пропускается с предупреждением. В парах после первой недопустимый шаблон отбрасывает только эту пару, с предупреждением в журнале.
- Режим JSONPath применяется только если тело является допустимым JSON.
- Правила перезаписи тела применяются к декодированному (UTF-8) тексту тела.
- К сообщению применяется только первое совпадающее правило для каждого направления (запрос / ответ). Если требуется несколько изменений, определите одно объединённое правило.
- Удаление несуществующего пути JSONPath оставляет тело неизменным. Установка значения создаёт ключ, если его родительский объект существует; если промежуточный путь отсутствует, ничего не происходит.
Эта страница — перевод английской версии. При расхождениях приоритет имеет английская версия.