Исследователь, публикующийся под псевдонимом cereblab, обнаружил, что консольная утилита xAI для написания кода — Grok Build — отправляла на серверы компании не отдельные файлы, нужные модели для работы, а целые Git-репозитории вместе с полной историей коммитов. Тестировалась версия 0.2.93. Метод простой: перехватить трафик во время загрузки, вытащить из перехваченного запроса git-бандл и посмотреть, что в нём лежит. В бандле нашёлся файл, который агенту явно запрещалось открывать.
Оказалось, что инструмент использует два независимых канала передачи данных. Первый — обычный «диалог» с моделью через эндпоинт /v1/responses, туда идёт то, что модель реально обрабатывает для выполнения задачи. Второй — отдельный канал загрузки в хранилище через /v1/storage. На репозитории объёмом 12 гигабайт разница получилась пугающей: модель обменялась с сервером около 192 килобайт трафика, а через канал хранилища ушло 5,10 гибибайта. Разрыв — примерно в 27 800 раз. Данные передавались 73 порциями примерно по 75 мегабайт каждая, и каждый запрос благополучно получал ответ HTTP 200. При тестах с репозиториями разного размера объём загрузки менялся прямо пропорционально размеру самого репозитория — то есть это не случайность и не единичный сбой, а системное поведение.
Пунктом назначения оказался бакет с говорящим названием grok-code-session-traces — жёстко прописанный прямо в бинарнике программы и в подготовленном файле metadata.json. Пути к отдельным файлам вели по адресу .
Чтобы проверить гипотезу окончательно, cereblab подложил в тестовый репозиторий файл src/_probe/never_read_canary.txt с уникальной меткой внутри и пометкой «никогда не читать». Файл в неизменном виде обнаружился в перехваченном бандле — вместе со всей историей коммитов репозитория. Эксперимент повторили на втором, никак не связанном с первым репозитории — результат тот же. Здесь важна оговорка: перехват доказывает лишь факт передачи, приёма и хранения данных. Он не доказывает, что xAI обучала модель на этом коде или что сотрудники компании его читали. Файлы, указанные в.gitignore, в бандл не попадали — туда уходили только отслеживаемые файлы вместе с историей.
Отдельная проблема касается секретов. Когда Grok читает какой-либо файл, его содержимое становится частью «модельного хода» — то есть попадает в диалог с моделью. Отслеживаемый.env-файл ушёл без всякой маскировки, вместе с тестовым API_KEY и тестовым DB_PASSWORD. То же самое содержимое обнаружилось и в архиве session_state, который отправлялся в хранилище. Подложенные секреты были синтетическими, реальные учётные данные в тестах не пострадали. Но суть проблемы от этого не меняется: любой файл с credentials, который агент открывает в ходе работы, передаётся и сохраняется без редактирования.
Отдельного разговора заслуживает переключатель приватности «Improve the model» — «улучшать модель». Cereblab отключил эту настройку и всё равно получил полную выгрузку репозитория. Эндпоинт /v1/settings при этом продолжал возвращать trace_upload_enabled: true. Разгадка в том, что этот тумблер контролирует только использование данных для обучения модели, но никак не влияет на то, покидает ли код машину пользователя вообще. Это два разных механизма контроля, и пользователю показывали только один из них — тот, что касается обучения.
Для сравнения cereblab прогнал через те же тесты конкурирующие инструменты. Claude Code не отправил ни одного бандла репозитория. Codex — тоже. Gemini в тесте на простое ожидание ничего не передал, а в тесте с реалистичной задачей упёрся в лимит запросов раньше, чем тест завершился, так что вывод по нему остался неопределённым. Grok Build оказался единственным исключением — именно он отправлял весь отслеживаемый репозиторий целиком вместе с историей. При этом стоит понимать: все облачные инструменты для написания кода что-то отправляют на сервер, идея «всё остаётся локально» не работает ни для одного из них. Но массовый сбор целого рабочего пространства — особенность именно Grok Build.
Здесь есть ещё один неприятный нюанс, касающийся истории коммитов. В репозитории нередко хранится проприетарный код, внутренние URL-адреса, данные клиентов и учётные данные, которые давно удалены из рабочего дерева файлов, но по-прежнему живут где-то в старых коммитах. Бандл захватывает всё это целиком, независимо от того, что видно в текущей версии файлов.
13 июля тот же самый бинарник версии 0.2.93, без единого обновления клиента, внезапно перестал отправлять запросы на /v1/storage. Cereblab перепроверил это шесть раз — ни одной загрузки. Сервер стал возвращать disable_codebase_upload: true и trace_upload_enabled: false. Независимо от него разработчик Peter Dedene подтвердил на своём аккаунте точно такое же изменение флагов — значит, дело не в единственной машине исследователя. Поскольку версия клиента не менялась, а изменились только серверные настройки, это явно переключение на стороне сервера, а не патч в приложении. xAI не подтвердила, коснулось ли исправление всех аккаунтов и является ли оно постоянным.
Компания вообще не выпустила формального security advisory или записи в changelog — вся коммуникация прошла через X (бывший Twitter). Аккаунт @SpaceXAI заявил, что корпоративные клиенты с включённой политикой Zero Data Retention никогда не сталкиваются с сохранением кода или трассировочных данных, и это касается и использования через API. Для обычных пользователей без ZDR предложили команду /privacy в CLI, которая отключает сохранение данных и удаляет уже синхронизированную информацию. Илон Маск лично заявил, что все данные пользователей, загруженные до исправления, будут «полностью и безвозвратно удалены», без остатка. Проблема в том, что политика Zero Data Retention покрывает только корпоративных клиентов и API — для обычных подписчиков единственным доступным инструментом остаётся команда /privacy.
Всем, кто пользовался Grok Build, стоит сменить учётные данные — в первую очередь те, что читал агент, что лежали в отслеживаемых файлах и что могли остаться в истории git, попавшей в бандл, включая секреты, которые были закоммичены, а затем удалены. Файлы из.gitignore в захват не попадали и остаются в безопасности, но закоммиченные файлы «прокатились» в истории репозитория, и последующее их удаление задним числом ничего не меняет — они уже сохранены где-то на серверах компании.
Позже, при отдельном анализе сборки 0.2.99, выяснилось, что код, отвечающий за загрузку, никуда не делся — он по-прежнему присутствует в бинарнике. Функция сейчас просто подавлена флагом на стороне сервера. Это значит, что xAI технически может включить загрузку заново удалённо, без выпуска нового обновления клиента. Компания так и не ответила на три главных вопроса: зачем изначально были нужны загрузки целых репозиториев по умолчанию, как долго хранились уже собранные данные и сколько пользователей это затронуло.
Оказалось, что инструмент использует два независимых канала передачи данных. Первый — обычный «диалог» с моделью через эндпоинт /v1/responses, туда идёт то, что модель реально обрабатывает для выполнения задачи. Второй — отдельный канал загрузки в хранилище через /v1/storage. На репозитории объёмом 12 гигабайт разница получилась пугающей: модель обменялась с сервером около 192 килобайт трафика, а через канал хранилища ушло 5,10 гибибайта. Разрыв — примерно в 27 800 раз. Данные передавались 73 порциями примерно по 75 мегабайт каждая, и каждый запрос благополучно получал ответ HTTP 200. При тестах с репозиториями разного размера объём загрузки менялся прямо пропорционально размеру самого репозитория — то есть это не случайность и не единичный сбой, а системное поведение.
Пунктом назначения оказался бакет с говорящим названием grok-code-session-traces — жёстко прописанный прямо в бинарнике программы и в подготовленном файле metadata.json. Пути к отдельным файлам вели по адресу .
Чтобы проверить гипотезу окончательно, cereblab подложил в тестовый репозиторий файл src/_probe/never_read_canary.txt с уникальной меткой внутри и пометкой «никогда не читать». Файл в неизменном виде обнаружился в перехваченном бандле — вместе со всей историей коммитов репозитория. Эксперимент повторили на втором, никак не связанном с первым репозитории — результат тот же. Здесь важна оговорка: перехват доказывает лишь факт передачи, приёма и хранения данных. Он не доказывает, что xAI обучала модель на этом коде или что сотрудники компании его читали. Файлы, указанные в.gitignore, в бандл не попадали — туда уходили только отслеживаемые файлы вместе с историей.
Отдельная проблема касается секретов. Когда Grok читает какой-либо файл, его содержимое становится частью «модельного хода» — то есть попадает в диалог с моделью. Отслеживаемый.env-файл ушёл без всякой маскировки, вместе с тестовым API_KEY и тестовым DB_PASSWORD. То же самое содержимое обнаружилось и в архиве session_state, который отправлялся в хранилище. Подложенные секреты были синтетическими, реальные учётные данные в тестах не пострадали. Но суть проблемы от этого не меняется: любой файл с credentials, который агент открывает в ходе работы, передаётся и сохраняется без редактирования.
Отдельного разговора заслуживает переключатель приватности «Improve the model» — «улучшать модель». Cereblab отключил эту настройку и всё равно получил полную выгрузку репозитория. Эндпоинт /v1/settings при этом продолжал возвращать trace_upload_enabled: true. Разгадка в том, что этот тумблер контролирует только использование данных для обучения модели, но никак не влияет на то, покидает ли код машину пользователя вообще. Это два разных механизма контроля, и пользователю показывали только один из них — тот, что касается обучения.
Для сравнения cereblab прогнал через те же тесты конкурирующие инструменты. Claude Code не отправил ни одного бандла репозитория. Codex — тоже. Gemini в тесте на простое ожидание ничего не передал, а в тесте с реалистичной задачей упёрся в лимит запросов раньше, чем тест завершился, так что вывод по нему остался неопределённым. Grok Build оказался единственным исключением — именно он отправлял весь отслеживаемый репозиторий целиком вместе с историей. При этом стоит понимать: все облачные инструменты для написания кода что-то отправляют на сервер, идея «всё остаётся локально» не работает ни для одного из них. Но массовый сбор целого рабочего пространства — особенность именно Grok Build.
Здесь есть ещё один неприятный нюанс, касающийся истории коммитов. В репозитории нередко хранится проприетарный код, внутренние URL-адреса, данные клиентов и учётные данные, которые давно удалены из рабочего дерева файлов, но по-прежнему живут где-то в старых коммитах. Бандл захватывает всё это целиком, независимо от того, что видно в текущей версии файлов.
13 июля тот же самый бинарник версии 0.2.93, без единого обновления клиента, внезапно перестал отправлять запросы на /v1/storage. Cereblab перепроверил это шесть раз — ни одной загрузки. Сервер стал возвращать disable_codebase_upload: true и trace_upload_enabled: false. Независимо от него разработчик Peter Dedene подтвердил на своём аккаунте точно такое же изменение флагов — значит, дело не в единственной машине исследователя. Поскольку версия клиента не менялась, а изменились только серверные настройки, это явно переключение на стороне сервера, а не патч в приложении. xAI не подтвердила, коснулось ли исправление всех аккаунтов и является ли оно постоянным.
Компания вообще не выпустила формального security advisory или записи в changelog — вся коммуникация прошла через X (бывший Twitter). Аккаунт @SpaceXAI заявил, что корпоративные клиенты с включённой политикой Zero Data Retention никогда не сталкиваются с сохранением кода или трассировочных данных, и это касается и использования через API. Для обычных пользователей без ZDR предложили команду /privacy в CLI, которая отключает сохранение данных и удаляет уже синхронизированную информацию. Илон Маск лично заявил, что все данные пользователей, загруженные до исправления, будут «полностью и безвозвратно удалены», без остатка. Проблема в том, что политика Zero Data Retention покрывает только корпоративных клиентов и API — для обычных подписчиков единственным доступным инструментом остаётся команда /privacy.
Всем, кто пользовался Grok Build, стоит сменить учётные данные — в первую очередь те, что читал агент, что лежали в отслеживаемых файлах и что могли остаться в истории git, попавшей в бандл, включая секреты, которые были закоммичены, а затем удалены. Файлы из.gitignore в захват не попадали и остаются в безопасности, но закоммиченные файлы «прокатились» в истории репозитория, и последующее их удаление задним числом ничего не меняет — они уже сохранены где-то на серверах компании.
Позже, при отдельном анализе сборки 0.2.99, выяснилось, что код, отвечающий за загрузку, никуда не делся — он по-прежнему присутствует в бинарнике. Функция сейчас просто подавлена флагом на стороне сервера. Это значит, что xAI технически может включить загрузку заново удалённо, без выпуска нового обновления клиента. Компания так и не ответила на три главных вопроса: зачем изначально были нужны загрузки целых репозиториев по умолчанию, как долго хранились уже собранные данные и сколько пользователей это затронуло.