Главная / Статьи

Никаких нарушений обнаружено при аудите 300 пакетов MCP npm.

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

Издатель: REACTAPP.TOP8 мин чтения1871 слов

Источники: I Audited 300 MCP-Related npm Packages. My Scanner Couldn’t Inspect Runtime Code in 217. So I created Driftward.

Триста пакетов npm с словом MCP в названии были извлечены из реестра и проанализированы в виде опубликованных архивов tarball, причем они так и не были установлены или запущены. Самым примечательным результатом стала не наличие вредоносного ПО, а сбой в охвате кода: у 217 из 300 пакетов (72,3%) в архиве присутствовал код для выполнения, однако каждый такой файл находился по пути, который сканер намеренно игнорирует, такому как dist/, build/ или пути с минифицированным кодом. Инструмент мог прочитать файлы manifest и README, сообщить о «спокойном» результате, но не изучить ни один из файлов, которые на самом деле должны были быть выполнены.

В итоге было выявлено 217 случаев недостаточного охвата кода, 80 пакетов с кодом для выполнения и 3 пакета, в которых вообще не было найдено кода для выполнения.

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

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

Здесь важен объем проверки. Результат означает, что один конкретный сканер не нашел ничего, что можно было бы прочитать, в 217 архивах формата tarball. Это не подразумевает, что эти пакеты вредоносны или небезопасны, а также что другой анализатор не смог бы их проверить.

Почему MCP повышает ставки

Локальный сервер MCP — это не просто библиотека, которую импортирует приложение. При использовании стандартного механизма передачи данных stdio клиент запускает его в качестве дочернего процесса. Политика безопасности проекта предусматривает, что сервер обладает теми же привилегиями на уровне среды, что и клиент, а механизм передачи данных stdio SDK не обеспечивает изоляции. Если агент может получить доступ к учетным данным, сетевому адресу или локальному инструменту, сервер без изоляции, скорее всего, сможет к ним тоже получить доступ.

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

Как был создан пример

Это был удобственный пример, а не полное исследование всей экосистемы.

Собиратель провёл 11 поисков в реестре npm, используя такие фразы, как mcp server, model context protocol, mcp connector и modelcontextprotocol. На один запрос приходилось до трёх страниц по 100 результатов; удалялись дубликаты, сохранялись названия, содержащие mcp, и количество результатов ограничивалось 300. Рейтинг поиска определяет, кто попадёт в результаты: один издатель может появляться многократно, а наличие в названии слова mcp не гарантирует наличие сервера MCP.

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

Сканер настроен на высокую точность. Он ищет сетевые API, точные URL-адреса хостов, вызовы подпроцессов, упоминания путей к учетным данным и ограниченный набор фраз, связанных с инъекциями, игнорируя при этом тестовый код, фикстуры, примеры, папки с компилированным выводом, минифицированные файлы, карты исходного кода и любые файлы размером более 1 МБ. Такой подход снижает количество ложных срабатываний, но также создает измеримые «слепые зоны».

Результаты

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

Выделяются четыре показателя:

  • Код во время выполнения, но отсутствуют подходящие файлы для его выполнения: 217 пакетов.
  • Из этих 217 пакетов у 107 была карта исходного кода, а у остальных 110 — её не было.
  • Сигнал сетевого API или literal-host в подходящем коде: 50 пакетов.
  • Соответствия в стиле вставки команд: 2, и после ручной проверки оба были признаны ложными срабатываниями.

Два ложных срабатывания

Фразы в стиле вставки команд были обнаружены в двух пакетах, и при ручном просмотре выяснилось, что оба содержат объяснительный текст. В одном из пакетов — сервер документации Mastra версии 1.2.22 — приводились примеры обработки с строками вроде IGNORE PREVIOUS INSTRUCTIONS. В другом пакете — сервер REMnux MCP версии 0.1.71 — сообщалось, что образцы вредоносного ПО могут содержать строки для вставки команд, и указывалось, как с ними обращаться аналитикам.

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

Злонамеренный пакет тестов с оценкой ноль

Один из пакетов использовался для контроля охвата. malicious-mcp-server 1.5.0 описывает себя как намеренно вредоносный сервер MCP для тестирования «конец-конец». В его архиве tarball находились три файла: package.json объемом 970 байт, README.md — 742 байта и dist/index.js — 610 358 байт.

При первом сканировании были прочитаны два небольших текстовых файла, файл dist/index.js был проигнорирован; не было обнаружено никаких признаков вредоносного поведения, поэтому пакету была присвоена оценка риска 0. Последующий подсчет сырых токенов в проигнорированном файле показал шесть случаев использования exec, два случая eval(), 52 случая использования base64 и три случая обращения к raw.githubusercontent.com.

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

Проблема была не в технике

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

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

  1. Был прочитан соответствующий код, и никакое правило не сработало.
  2. Был прочитан соответствующий код, но правила не смогли распознать его поведение.
  3. Соответствующий код так и не был прочитан.

Только первый вариант является значимым результатом чистой проверки, но даже он не является доказательством безопасности.

Что должно быть указано в хорошем отчете

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

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

    Почему поведение при выполнении все еще имеет значение

    Статический анализ представляет собой моментальную копию состояния. Поведение может измениться после обновления, и команда вроде npx package-name может запускать новый код, при этом локальная конфигурация MCP остается прежней. Поэтому инструменты управления во время выполнения дополняют статический анализ, а не конкурируют с ним. Каждый локальный сервер должен работать под собственным идентификатором с политикой минимальных привилегий. Его фактические цели и возможности должны регистрироваться, а базовый уровень должен быть явно утверждён. Последующие запуски сравниваются с собственной историей данного сервера, что позволяет выявить смещение идентификатора при изменении конфигурации, а также обеспечивает видимость обновления пакета при неизменной конфигурации, если изменятся возможности сервера во время выполнения.

    Один инструмент с открытым исходным кодом следует этой модели, оборачивая каждый сервер MCP в отдельного субъекта, например mcp:github, с собственной политикой, подписанным базовым профилем и записью отклонений. Он предназначен как резервный механизм во время выполнения для проверки исходного кода, но не может заменить её. Поддержка платформ различается: в macOS Seatbelt привязывает выходные сокеты к прокси с обратным подключением, тогда как в Linux bubblewrap пока обеспечивает ограничение файловой системы и процессов, а также переменные окружения прокси, но без аналогичной жесткой сетевой привязки.

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

    Ограничения аудита

    • Выбор осуществлялся на основе рейтинга в поиске и фильтрации по названию, поэтому выборка не является ни случайной, ни репрезентативной.
    • Все данные относятся к 1 сентября 2026 года, причем как содержимое npm, так и результаты поиска могут меняться.
    • Измерялись архивы в формате tarball, а не то, является ли каждый из них рабочим сервером MCP.
    • Фраза «scanner blind» относится к правилам исключения этого сканера, а не к общей статической анализу.
    • Проводился подсчёт карт источника, однако никто не проверял, содержит ли каждая карта пригодный для использования исходный код.
    • Буквальные URL-адреса и названия API являются статическими подсказками и не указывают на то, что тот или иной код выполняется.
    • Пакеты никогда не запускались, поэтому цели сетевых запросов во время выполнения остаются неизвестными.
    • Отсутствие находок не является вердиктом о безопасности.

    Воспроизведение запуска

    Чтобы воссоздать точный набор архивов tarball, используйте файл с зафиксированными результатами в качестве исходных данных:

    # self-bootstrapping — no install needed
    git clone https://github.com/abdalhafeezbushara/driftward.git
    cd driftward
    python3 research/mcp-npm-audit-2026-09-01/audit.py \
      research/mcp-npm-audit-2026-09-01/results.json \
      /tmp/mcp-audit-rerun.json
    

    Чтобы получить свежий снимок состояния в 300 пакетов из текущих результатов поиска npm:

    N=300 ./detonate/fetch-corpus.sh /tmp/mcp-packages.txt
    python3 research/mcp-npm-audit-2026-09-01/audit.py \
      /tmp/mcp-packages.txt /tmp/mcp-audit-current.json
    

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

    Краткий чек-лист для пользователей MCP

    • Фиксируйте версии. Точная версия вместе с значением целостности лучше, чем использование команды npx без указания версии.
    • Проверяйте опубликованный результат. Чистый репозиторий мало чего стоит, если установленный архив содержит генерированный пакет, который никто не открывал.
    • Спрашивайте данные об охвате тестирования. Если в отчете нет файлов, подлежащих тестированию, считайте их недоступными, а не чистыми.
    • Изолируйте каждый сервер отдельно, задавая ему собственную идентификацию, диапазон файловой системы и список разрешенных хостов, независимо от того, работает ли он локально или через мост.
    • Сделайте процесс установления доверия осознанным шагом: запишите пример корректной работы, подпишите этот эталон и заблокируйте процесс CI или потребуйте пересмотра при изменении возможностей.

    Отказ от статического анализа — это не урок, важно указать степень его охвата. Для серверов MCP и подобных инструментов, используемых агентами, чтение исходного кода, анализ сгенерированных результатов, проверка происхождения данных, использование песочницы, запись поведения при выполнении и выявление отклонений — всё это охватывает разные аспекты одной проблемы. Самая пугающая строка в отчете о безопасности может быть не обнаружением вредоносного ПО, а отсутствием результатов проверок, что заставляет задаться вопросом: отсутствие результатов для какого объема кода?