Головна / Статті

Жодних знахідок щодо обсягу коду? Аудит 300 пакетів MCP npm

Під час аудиту архіву tar з 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 без вказання версії.
    • Перевіряйте опублікований продукт. Чистий репозиторій мало що значить, якщо встановлений архів tarball містить створений пакет, який ніхто не відкривав.
    • Запитуйте дані про рівень покриття. Якщо у звіті немає файлів, які підлягають перевірці, вважайте його недоступним, а не чистим.
    • Ізолюйте кожен сервер окремо, надаючи йому власну ідентичність, обмеження файлової системи та дозволені хости, незалежно від того, чи він працює локально, чи через міст.
    • Робіть процес надання довіри свідомим кроком: записуйте приклад правильної роботи, підписуйте цей базовий стан та блокуйте процес CI або вимагайте перевірки при зміні функціоналу.

    Відмова від статичного аналізу — це не урок; важливо вказати рівень його охоплення. Для серверів MCP та подібних інструментів, якими користуються агенти, читання вихідного коду, аналіз створених результатів, перевірка походження даних, робота в пісочниці, запис поведінки програми під час виконання та виявлення відхилень — усе це охоплює різні аспекти однієї проблеми. Найстрашнішою фразою у звіті про безпеку може бути не виявлення шкідливого програмного забезпечення, а відсутність знахідок, що залишає без відповіді наступне питання: відсутність знахидок для якої кількості коду?