Наприкінці жовтня GitHub Advisory Database додала дві вразливості в пакетах, які часто трапляються в Laravel-проєктах. GHSA-9vw2-f4vp-x7gf (CVE-2026-119193, середня серйозність) - у PHPUnit 13.4: інший користувач тієї самої машини міг виконати свій код із правами розробника. GHSA-2v3c-qxhr-7vpv (CVE-2026-109413, висока) - у symfony/yaml: один документ вичерпує стек і валить процес.
Жодна з них не робить вразливим кожен Laravel-застосунок, як нещодавній ReDoS у HttpFoundation. Але одна зачіпає всіх, хто перейшов на Pest 5, і може зламати composer audit у CI.
Що сталося в PHPUnit 13.4
PHPT - формат тестів, у якому код і очікуваний вивід лежать в одному файлі; його використовують автори бібліотек і розширень. Коли PHPT-тести запускаються зі збором покриття коду (PCOV або Xdebug), PHPUnit записує код тесту в окремий файл і виконує його в дочірньому процесі.
Починаючи з 13.4.0 ці файли опинялися в каталозі з фіксованою назвою - sys_get_temp_dir() . '/phpunit-phpt', тобто /tmp/phpunit-phpt на Linux. Якщо каталог уже існував, PHPUnit використовував його, не перевіряючи власника і права. Інший користувач машини міг створити цей каталог заздалегідь, дочекатися появи файлу з тестом і підмінити його своїм кодом. Той виконувався з правами розробника, а потім запускав оригінальний тест, тож тест проходив, і звіт про покриття був повним. У попередженні є повний proof of concept.
PHPUnit 13.4.4 створює для цих файлів каталог із непередбачуваною назвою, доступний лише поточному користувачеві, і не використовує каталог, який уже існує. У тому ж релізі виправили схожу проблему з каталогом кешу статичного аналізу, а 13.4.5 доробив цей фікс для систем без розширення POSIX.
Хто насправді під загрозою
Щоб атака спрацювала, мають збігтися всі умови:
- PHPUnit 13.4.0-13.4.3. Версії 12.5 і старші не вразливі: вони пишуть тимчасові файли поруч із PHPT-тестом;
- у проєкті є PHPT-тести, і їх запускають зі збором покриття;
/tmp спільний для кількох користувачів. macOS і Windows за замовчуванням використовують окремий тимчасовий каталог для кожного користувача, тож там проблеми немає;
- у зловмисника є обліковий запис на тій самій машині або контроль над процесом, що працює від іншого користувача, і він встиг створити каталог першим.
Будемо чесними: типовий Laravel-застосунок PHPT-тестів не має, а стандартний скелет laravel/laravel досі вимагає phpunit/phpunit ^12.5.12, тобто вразливої гілки взагалі не ставить. Одноразовий контейнер у CI, де працює один користувач, теж не вразливий. Реальний ризик - на спільних dev-серверах і self-hosted CI-раннерах, де на одній машині працюють різні облікові записи, і для авторів пакетів, які тестують PHPT.
Чому Pest 5 треба оновлювати окремо
Найпомітніше ця вразливість зачіпає користувачів Pest 5. Pest закріплює PHPUnit на одній patch-версії: пара require і conflict у його composer.json не дозволяє взяти новішу. Pest 5.3.1, 5.3.2 і 5.3.3 прив'язані відповідно до 13.4.1, 13.4.2 і 13.4.3 - тобто саме до вразливих версій. Тому composer update phpunit/phpunit нічого не дасть, доки ви на старому Pest.
Як пояснено в PR із фіксом, через це composer audit падає на попередженні і ламає CI, а Composer, який блокує встановлення версій із відомими вразливостями, на гілці 5.x взагалі не міг розв'язати залежності без --no-blocking. Pest 5.3.4 підняв PHPUnit одразу до 13.4.5, а не до 13.4.4, бо, за словами автора PR, 13.4.5 доводить фікс до систем без розширення POSIX. Поточний Pest 5.3.5 теж вимагає ^13.4.5.
composer show pestphp/pest phpunit/phpunit
composer update pestphp/pest --with-all-dependencies
Якщо ви на PHPUnit 13.4 без Pest, достатньо звичайного composer update phpunit/phpunit.
symfony/yaml: коротко, що нового
Про цю вразливість ми вже коротко писали в огляді 28 жовтня: Yaml\Parser::lexInlineStructure() рекурсивно розбирав вкладені inline-колекції на кшталт [[[[...]]]], не враховуючи maxNestingLevel, і достатньо глибокий документ вичерпував стек. Тепер лексер рахує ту саму глибину, що й решта парсера, і за межею кидає ParseException. Виправлені версії: 5.4.54, 6.4.45, 7.4.18 і 8.1.6, так само для symfony/symfony.
Кілька деталей, які варто додати:
- Фікс злили ще 29 серпня як «hardening», а 6.4.45, 7.4.18 і 8.1.6 вийшли 30 серпня. Попередження опублікували лише 27 жовтня. Якщо ви оновлювали Symfony-пакети з вересня, на гілках 6.4-8.1 виправлення, найімовірніше, вже стоїть.
- Сам Laravel
symfony/yaml не потребує, але пакет часто приходить транзитивно. Наприклад, його напряму вимагають darkaonline/l5-swagger, zircote/swagger-php і knuckleswtf/scribe. Вони розбирають ваші власні специфікації, а не дані від відвідувачів, тож саме по собі це не робить застосунок вразливим.
- Найгірший сценарій у Symfony-світі -
#[MapRequestPayload] без обмеження acceptFormat: там вистачало одного неавтентифікованого запиту з YAML. У Laravel аналога немає, тож ризик з'являється лише тоді, коли ваш код сам викликає Yaml::parse() на тому, що надіслав користувач: імпорт конфігурацій, OpenAPI-специфікацій, front matter.
composer why symfony/yaml
composer update symfony/yaml
Що зробити сьогодні
- Запустіть
composer audit: обидва попередження вже в базі, і він покаже, чи вони стосуються вас.
- На Pest 5 оновіть сам Pest до 5.3.4 або новішого: PHPUnit підтягнеться разом із ним.
- На PHPUnit 13.4 без Pest оновіться до 13.4.4 або новішого. На PHPUnit 12.5 нічого робити не треба.
- Якщо тести ганяються на спільній машині, сенс має й ширший захід: окремий
TMPDIR для кожного користувача або PrivateTmp у systemd закривають увесь клас таких атак, а не лише цю.
symfony/yaml оновіть, якщо він є в composer.lock, і окремо перевірте, чи застосунок приймає YAML від користувачів.