Безопасность фоновых задач и очередей: что проверить перед запуском
Воркеры, брокеры и job-payload — частая слепая зона AppSec, которую легко закрыть до релиза
Введение
Фоновые задачи и очереди — это инфраструктура, о которой обычно вспоминают в последнюю очередь. Пока веб-приложение проходит через WAF, статический анализ и ревью, воркер, который слушает Redis или RabbitMQ и выполняет «отправить письмо», «сгенерировать отчёт» или «импортировать файл по ссылке», часто живёт по своим правилам: меньше код-ревью, более широкие сетевые права, доступ к секретам базы данных и внешних API. Именно поэтому очереди регулярно становятся точкой входа в реальных инцидентах — от майнинговых ботнетов через незащищённый Redis до RCE через десериализацию задачи [1][15].
Материал собирает практический чек-лист: что стоит проверить до того, как фоновая обработка уедет в продакшен — для CTO, который оценивает архитектурные риски, и для разработчика, который пишет конкретный обработчик задачи.
Что вообще считается «фоновой задачей»
Базово background job запускается одним из двух способов: по событию (новое сообщение в очереди) или по расписанию (cron-подобный таймер) [2]. Архитектурно это означает разделение компонентов: продюсер кладёт сообщение в очередь, воркер его забирает и выполняет. Это разделение даёт устойчивость — если воркер падает, сообщение остаётся в очереди и будет обработано позже — но оно же создаёт отдельную границу доверия между «что положили в очередь» и «что выполнит код воркера» [2].
Важно: фоновые задачи часто получают доступ к ресурсам, которым веб-слой не должен иметь прямого доступа — внутренним базам данных, файловым хранилищам, сторонним API с повышенными правами. Поэтому изоляция воркера в отдельном сетевом сегменте с более узкими разрешениями — рекомендуемая практика, а не излишество [2].
Слой 1: сам брокер — то, что чаще всего остаётся открытым
Redis без пароля и с публичным bind
Это, пожалуй, самая частая и самая дорогая ошибка конфигурации очередей. Классический уязвимый конфиг выглядит так: bind 0.0.0.0, protected-mode no, пустой пароль или ACL-пользователь с nopass [16]. Результат предсказуем: автоматизированные сканеры находят такой инстанс за часы, а не дни.
Что известно из реальных кампаний: ботнет P2PInfect компрометирует Kubernetes-кластеры именно через незащищённые Redis-инстансы, используя в том числе критическую уязвимость Lua sandbox escape CVE-2022-0543 с CVSS 10.0 [15][1]. По данным анализа облачных окружений Wiz, в интернете обнаружено порядка 330 000 публично доступных инстансов Redis, из них около 60 000 — вообще без аутентификации [17]. Отдельно стоит уязвимость RediShell (CVE-2025-49844) — критическая RCE-проблема в Lua-интерпретаторе, затрагивающая все версии Redis [17].
Что делать:
- Никогда не публиковать Redis/RabbitMQ напрямую в интернет; доступ — только из приватной сети или через VPN/VPC-эндпоинт.
- Включать аутентификацию (пароль или ACL) даже для «временных» и dev-окружений — именно они чаще всего забываются открытыми.
- Обновлять Redis до версий, закрывающих известные RCE — отставание от патчей напрямую конвертируется в риск компрометации [17].
SQS, managed-очереди в облаке
Для управляемых очередей риск смещается с «открытого порта» на IAM и шифрование. AWS прямо рекомендует: очередь не должна быть публично доступной, доступ — через IAM-роли с минимально необходимыми правами, включено серверное шифрование (SSE-SQS или SSE-KMS), принудительно используется TLS для соединений [18][19]. Те же правила должны применяться и к DLQ — на практике её часто забывают шифровать и ограничивать так же строго, как основную очередь [19].
Отдельный нюанс: при шифровании ключом, управляемым клиентом (SSE-KMS), нужно явно прописать в политике ключа разрешение для самого сервиса очередей на использование ключа — иначе обработка сообщений сломается на проде, а не на стадии тестирования [20].
Слой 2: что приезжает в payload — десериализация и инъекции
Если задача сериализуется библиотечными механизмами вроде pickle в Python, ObjectInputStream в Java или unserialize() в PHP, а producer задачи — не полностью доверенный источник (например, payload собирается из пользовательского ввода или приходит от внешнего сервиса), возникает классический класс уязвимостей — небезопасная десериализация. Документация самого модуля pickle прямо предупреждает: никогда не десериализуйте данные из недоверенного источника [9].
Механизм атаки концептуально прост: сериализованный объект может содержать инструкцию, которая выполнится в момент восстановления объекта, а не в момент его использования — то есть код может выполниться ещё до того, как приложение успеет что-либо провалидировать [11]. В Python это магический метод __reduce__, в Java — gadget-цепочки через readObject(), использующие классы из распространённых библиотек вроде Apache Commons Collections [12].
Практический чек-лист по десериализации в очередях:
- Аудит точек десериализации: искать
pickle.loads,yaml.loadбез safe-loader,unserialize(),ObjectInputStreamво всех обработчиках задач [9]. - Определить, откуда реально приходят данные в payload — если источник включает что-то, на что может повлиять внешний пользователь (HTTP-запрос, файл, очередь, к которой имеет доступ сторонний сервис), считать payload недоверенным [9].
- Заменить pickle/native-сериализацию на JSON или другой формат без возможности выполнения кода — это базовая рекомендация и для Celery-подобных систем, и для RPC между сервисами [8][3].
- Если кастомная сериализация неизбежна — подписывать сообщения (HMAC) и проверять подпись до десериализации, ограничивать allow-list типов, которые разрешено восстанавливать [11][3].
- Не полагаться на «у нас внутренняя сеть, untrusted данные туда не попадут» как на единственный барьер — для очередей это предположение особенно часто оказывается неверным, потому что в брокер может писать больше сервисов, чем изначально предполагалось.
Слой 3: идемпотентность и поведение при сбое
Большинство брокеров дают гарантию доставки at-least-once, а не exactly-once. Это значит: сообщение может быть обработано дважды — например, если воркер успел выполнить работу, но не успел подтвердить (ack) до сбоя сети. С точки зрения безопасности и целостности данных это не «эксплуатация», но прямой риск: повторная отправка письма, двойное списание средств, дублирующаяся запись в БД [21][22].
Базовое требование: обработчик задачи должен быть безопасен к повторному запуску. Практически это означает дедупликацию по идентификатору сообщения или ключу идемпотентности и хранение состояния «уже обработано» отдельно от самой бизнес-логики [21][22].
Poison messages и Dead Letter Queue
Сообщение, которое стабильно проваливает обработку — из-за повреждённого формата, ссылки на несуществующий объект или бага в коде обработчика — без специального механизма будет ретраиться бесконечно, блокируя capacity воркера и, в системах с гарантией порядка, всю последующую очередь [23][22]. Решение — Dead Letter Queue: после исчерпания лимита попыток или классификации ошибки как непереходящей сообщение уходит в отдельную очередь для разбора человеком [13][23].
Важные детали эксплуатации DLQ, которые редко документируют, но регулярно нарушают на практике:
- Разделять transient-ошибки (таймаут, временная недоступность зависимости) и permanent-ошибки (невалидная схема, нарушение бизнес-правила) — ретраить стоит только первые [13][22].
- DLQ — не архив и не помойка: задавать retention и владельца очереди, иначе она превращается в «кладбище», где сообщения копятся месяцами без анализа [14].
- Replay из DLQ обязан учитывать идемпотентность и ограничиваться по объёму — «отправить всё обратно одной командой» — один из самых частых сценариев повторного инцидента [14][24].
- Если в payload бывают персональные данные, к DLQ применяются те же требования по защите, что и к основной очереди: шифрование, редактирование чувствительных полей, контроль retention [22].
Слой 4: секреты и конфигурация воркера
Воркеры обычно получают доступ к учётным данным БД, ключам сторонних API, токенам отправки писем — то есть к секретам не меньшей чувствительности, чем у веб-приложения, а иногда и большей. Стандартный способ — переменные окружения — удобен, но имеет системные ограничения с точки зрения безопасности.
OWASP Secrets Management Cheat Sheet прямо отмечает: переменные окружения доступны любому процессу в том же пространстве пользователя, часто попадают в логи и системные дампы, и их использование не рекомендуется, если есть альтернатива [8]. Дополнительная проблема — наследование: переменные окружения автоматически передаются дочерним процессам, включая сторонние утилиты, которые вызывает обработчик задачи, что расширяет поверхность утечки за пределы исходного процесса [9].
Практические шаги:
- Не логировать переменные окружения целиком при отладке и не оставлять
set -x/verbose-режимы включёнными в продакшен-конфигурации воркеров — это типичный канал утечки секретов в build- и runtime-логи [9][8]. - Для продакшен-нагрузок предпочитать выделенные secret-менеджеры (Vault, облачные Secrets Manager) вместо плоских переменных окружения там, где это архитектурно возможно [9].
- Ротация ключей доступа к брокеру и внешним сервисам по расписанию — стандартная рекомендация и для SQS/IAM-доступа в частности [19].
- Разделять секреты между типами задач: обработчик отправки email не должен по умолчанию иметь доступ к ключам платёжного провайдера, даже если они «лежат рядом» в общей конфигурации.
Слой 5: исходящие запросы из обработчика — SSRF
Если фоновая задача делает исходящий HTTP-запрос по адресу, который пришёл в её payload — типичные кейсы: вебхуки, импорт изображения или файла по ссылке, генерация превью URL — возникает классический вектор Server-Side Request Forgery. OWASP описывает SSRF как ситуацию, когда сервер получает возможность обратиться к произвольному ресурсу по URL, заданному злоумышленником, даже если этот ресурс защищён файрволом или недоступен напрямую из интернета [25].
Для воркеров риск зачастую выше, чем для веб-фронта: фоновые процессы исторически получают более широкий сетевой доступ «для удобства», и именно через такой запрос атакующий может достать сервис метаданных облака (169.254.169.254) и получить временные IAM-учётные данные — ровно так был скомпрометирован Capital One в одном из самых известных инцидентов SSRF [26].
Защитные меры, рекомендуемые OWASP Cheat Sheet по SSRF [27]:
- Сегментировать сетевой доступ: функциональность, которая делает запросы по внешним URL, должна находиться в отдельном сегменте сети с ограниченным доступом к внутренним ресурсам.
- Применять политику «запрещено по умолчанию» на уровне сетевых правил, разрешая только необходимый исходящий трафик.
- Где возможно — использовать allow-list доменов; для случаев с произвольными внешними URL (вебхуки) allow-list по IP редко применим, и тогда основной барьер — сетевая сегментация, а не валидация на уровне приложения [27].
- Не доверять результату DNS-резолва один раз: переадресации и DNS rebinding позволяют обойти проверку, сделанную до запроса, если сам HTTP-клиент резолвит адрес заново [28].
Слой 6: зависимости воркера и AI-кодинг
Обработчики фоновых задач — обычно код, который реже перечитывают построчно: он скучный, типовой, написан «один раз и забыт». Это делает его удобной целью для рисков supply chain, включая новый вектор, специфичный именно для AI-ассистированной разработки.
Системный обзор по теме package hallucinations показывает: модели кода действительно «придумывают» несуществующие имена пакетов — в среднем по нескольким протестированным моделям частота халлюцинаций пакетов составила около 19,6% [29], а у моделей с открытым кодом этот показатель может достигать 21,7% против 5,2% у моделей серии GPT [30]. Термин для атаки, эксплуатирующей это — slopsquatting: атакующий заранее регистрирует правдоподобное, но не существовавшее имя пакета в PyPI или npm и наполняет его вредоносным кодом в расчёте на то, что разработчик, доверяющий ассистенту, установит его не глядя [31].
Это явление пока не задокументировано как масштабно эксплуатируемое в реальных атаках — по состоянию на апрель 2025 года подтверждённых случаев эксплуатации slopsquatting не зафиксировано, хотя исследователи оценивают потенциал атаки как высокий и предсказуемый [31]. Но смежные, уже реализованные атаки на supply chain показывают масштаб риска: атака GhostAction затронула 327 пользователей GitHub и 817 репозиториев, в результате злоумышленники похитили 3325 секретов, включая токены доступа к PyPI, npm и DockerHub [32].
Что проверить конкретно для очередей и фоновых задач:
- Каждая новая или обновлённая зависимость воркера — особенно если она была предложена AI-ассистентом — должна быть проверена на существование в реестре, историю публикаций и репутацию мейнтейнера до того, как попадёт в lock-файл [31].
- Использовать приватные реестры/зеркала с проверкой происхождения пакета вместо прямой установки из публичных реестров в build-пайплайне воркера.
- Сканировать изменения в lock-файлах (package-lock.json, requirements.txt/poetry.lock и аналоги) как часть код-ревью PR, а не полагаться на то, что «зависимости не менялись».
- Отдельно контролировать транзитивные зависимости — именно они чаще всего проезжают незамеченными при беглом ревью diff'а.
Слой 7: административные интерфейсы воркеров
У многих систем фоновых задач есть встроенный web UI для мониторинга очереди (например, у Sidekiq и расширений вроде sidekiq-unique-jobs). Это полезный инструмент для операционной работы и одновременно отдельная поверхность атаки, если её забыли закрыть авторизацией.
Конкретный пример: уязвимость CVE-2024-25122 в sidekiq-unique-jobs — reflected XSS с критической оценкой CVSS 10.0, эксплуатируемая через специально сформированные GET-параметры к эндпоинтам административного UI, способная привести к краже cookie, сессии и данных local storage [33][34]. Более ранняя CVE-2023-1892 описывает аналогичный класс XSS-проблем в самом Sidekiq [35].
Что проверить:
- Admin/monitoring UI воркера никогда не должен быть доступен без аутентификации и желательно — без выхода за пределы внутренней сети или VPN.
- Версии библиотек мониторинга очередей должны обновляться так же дисциплинированно, как и сама бизнес-логика — экспозиция таких UI обычно остаётся незамеченной месяцами именно потому, что они считаются «второстепенным» компонентом.
- Принцип наименьших привилегий применим и здесь: даже легитимные пользователи admin-панели не должны иметь возможности произвольно манипулировать очередью без логирования действия.
Чек-лист перед запуском фоновой обработки в продакшен
Перед merge/release стоит явно пройтись по следующим пунктам:
- Брокер (Redis/RabbitMQ/Kafka) не доступен из публичного интернета; аутентификация включена везде, включая dev и staging.
- Для managed-очередей (SQS и аналоги) включено шифрование at-rest и enforced TLS in-transit, применена политика наименьших привилегий через IAM.
- Ни один обработчик задачи не десериализует payload небезопасными механизмами (pickle, native-сериализация) без проверки источника или цифровой подписи.
- Все обработчики идемпотентны: повторная доставка одного и того же сообщения не приводит к дублирующимся побочным эффектам.
- Настроена DLQ с разумным лимитом ретраев, отдельным владельцем и процедурой контролируемого replay.
- Секреты не хранятся в виде «голых» переменных окружения там, где доступен secret-менеджер; логирование окружения целиком запрещено.
- Исходящие запросы по URL из payload изолированы сетевой сегментацией и/или allow-list; нет прямого доступа из воркера к сервису метаданных облака.
- Зависимости воркера, включая предложенные AI-ассистентом, проверены на существование в реестре и репутацию перед добавлением в lock-файл.
- Административные/мониторинговые UI очередей закрыты аутентификацией и недоступны публично; версии библиотек актуальны.
- Все эти пункты — часть ревью PR, а не отдельного «security-аудита раз в квартал»: риск приезжает вместе с конкретным изменением, и проверять его эффективнее до merge, чем после инцидента.
Ошибки, которые встречаются чаще всего
- «Это внутренняя очередь, она не публикуется» — но конфигурация bind и пароль это не подтверждают; проверка должна быть техническая, а не основанная на намерении.
- «Мы используем JSON, у нас нет pickle» — но если в коде где-то остался legacy-обработчик с native-сериализацией для обратной совместимости, именно он окажется незамеченным при ревью.
- «DLQ настроена» — но никто не смотрит её объём и не знает, кто отвечает за разбор; DLQ без owner'а функционально равна потере сообщений с задержкой.
- «Секреты в .env, это же стандарт» — стандарт распространённый, но не безопасный по умолчанию; разница между «удобно» и «безопасно» здесь принципиальна [8][36].
- «AI предложил пакет — значит, он существует» — особенно опасное предположение именно для редко перечитываемого кода фоновых обработчиков [31].
Где здесь уместен дополнительный слой контроля
Большая часть перечисленных рисков — деталь конкретного изменения: новая зависимость в обработчике задачи, передача payload через pickle вместо JSON, обработчик, который начал делать HTTP-запрос по динамическому URL, конфиг воркера с расширенным сетевым доступом. Такие изменения почти никогда не выглядят как «security-PR» — это обычный рабочий пул-реквест, который проходит ревью наравне с остальными, и именно поэтому риск легко не заметить за пределами строк, которые реально читают [37].
HighVibe в этом контексте — не замена SAST/SCA/CI-проверок и не полное сканирование репозитория «на всякий случай», а дополнительный слой контроля непосредственно перед merge и релизом: анализ конкретного изменения в коде, конфигурации и зависимостях, включая служебные файлы и task-обработчики, которые обычно читают хуже всего [37]. Применительно к очередям это означает: при появлении новой или изменённой зависимости в обработчике задачи, при переходе на менее безопасный механизм сериализации или при расширении сетевого доступа воркера — сигнал об этом виден до того, как изменение уедет в продакшен, а не после инцидента с разбором постмортема.
Заключение
Фоновые задачи и очереди реже попадают под пристальный security-ревью, чем веб-эндпоинты, но по факту имеют не меньший — а часто больший — доступ к чувствительным данным и инфраструктуре. Большинство разобранных рисков не требуют экзотических знаний: закрытый доступ к брокеру, безопасная сериализация, идемпотентность, DLQ с владельцем, секреты вне переменных окружения, контроль исходящих запросов и зависимостей. Дальше — вопрос дисциплины: проверять эти пункты системно при каждом изменении обработчика, а не вспоминать о них после инцидента.
Источники
[1] FortiGuard Labs — "Misconfigured, Enrolled and Dormant: Anatomy of a P2Pinfect Kubernetes Compromise" — https://www.fortinet.com/uk/blog/threat-research/misconfigured-enrolled-and-dormant-anatomy-of-a-p2pinfect-kubernetes-compromise
[2] Microsoft Learn — "Best Practices for Background Jobs - Azure Architecture Center" — https://learn.microsoft.com/en-us/azure/architecture/best-practices/background-jobs
[3] Xygeni — "How Insecure Deserialization Becomes Remote Code Execution" — https://xygeni.io/blog/how-insecure-deserialization-becomes-remote-code-execution/
[4] GitHub Issue dmlc/dgl — "Vulnerability: Remote Code Execution by Pickle Deserialization via rpc.recv_request()" — https://github.com/dmlc/dgl/issues/7874
[5] DesignGurus — "How do you implement DLQs and handle poison messages?" — https://www.designgurus.io/answers/detail/how-do-you-implement-dlqs-and-handle-poison-messages
[6] Rahul Dhar — "A Layman's Guide to Dead Letter Queues (DLQ) with Google Pub/Sub" — https://www.rahuldhar.me/p/a-laymans-guide-to-dead-letter-queues
[7] AlgoMaster — "Dead Letter Queues | System Design" — https://algomaster.io/learn/system-design/dead-letter-queues
[8] CyberArk Developer — "Environment Variables Don't Keep Secrets" — https://developer.cyberark.com/blog/environment-variables-dont-keep-secrets-best-practices-for-plugging-application-credential-leaks/
[9] GitGuardian Blog — "Best Practices for Environment Variables Secrets Management" — https://blog.gitguardian.com/secure-your-secrets-with-env/
[10] OWASP API Security Top 10 — "API7:2023 Server Side Request Forgery" — https://owasp.org/API-Security/editions/2023/en/0xa7-server-side-request-forgery/
[11] Secure-Cookie — "Remote Code Execution - Insecure Deserialization" — https://secure-cookie.io/attacks/insecuredeserialization/
[12] FBI Support Cyber Law Knowledge Base — "How Do Insecure Deserialization Vulnerabilities Lead to Remote Code Execution?" — https://fbisupport.com/insecure-deserialization-vulnerabilities-lead-remote-code-execution/
[13] Abstract Algorithms — "Dead Letter Queue Pattern: Isolating Poison Messages and Recovering Safely" — https://www.abstractalgorithms.dev/dead-letter-queue-pattern-poison-message-recovery
[14] Software Engineer's Notes — "Dead Letter Queues (DLQ): The Complete, Developer-Friendly Guide" — https://swenotes.com/2025/09/25/dead-letter-queues-dlq-the-complete-developer-friendly-guide/
[15] CyberSecurityNews — "P2PInfect Botnet Compromises Kubernetes Clusters Through Exposed Redis Instances" — https://cybersecuritynews.com/p2pinfect-botnet-compromises-kubernetes-clusters-through-exposed-redis-instances/
[16] Wiz Blog — "Detect critical application misconfiguration risks" — https://www.wiz.io/blog/detect-critical-application-misconfiguration-risks
[17] Wiz Blog — "Wiz Finds Critical Redis RCE Vulnerability: CVE-2025-49844" — https://www.wiz.io/blog/wiz-research-redis-rce-cve-2025-49844
[18] AWS Documentation — "Amazon SQS security best practices" — https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-security-best-practices.html
[19] SystemsArchitect.io — "AWS SQS Security Best Practices" — https://www.systemsarchitect.io/services/aws-sqs/security-best-practices
[20] OneUptime — "How to Configure SQS Access Policies" — https://oneuptime.com/blog/post/2026-02-02-sqs-access-policies/view
[21] PlantagoWeb — "Интеграция Celery и RabbitMQ" — https://plantagoweb.ru/blog/integratsiya-s-celery-i-rabbitmq-dlya-fonovykh-zadach-8c27d1/
[22] DataLopata — "Очередь задач celery: архитектура и оптимизация систем в 2026" — https://datalopata.ru/blog/ochered-zadach-celery-arhitektura-i-optimizatsija-sistem-v-2026/
[23] Moments Log — "Dead Letter Queue Triage Checklist" — https://www.momentslog.com/development/dead-letter-queue-triage-checklist-how-to-keep-failed-messages-from-becoming-silent-data-loss
[24] Cogin — "Surviving poison messages in MSMQ" — https://www.cogin.com/articles/SurvivingPoisonMessages.php
[25] OWASP Foundation — "Server Side Request Forgery" — https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
[26] Medium (Emmanuelnnebedum) — "#7: Server Side Request Forgery (SSRF)" — https://medium.com/@emmanuelnnebedum704/7-server-side-request-forgery-ssrf-7296adf203a9
[27] OWASP Cheat Sheet Series — "Server Side Request Forgery Prevention" — https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
[28] APIsec — "SSRF Explained: OWASP API Security Principle 7 Made Simple" — https://www.apisec.ai/blog/server-side-request-forgery-ssrf-owasp-api-security-principle-seven-explained
[29] USENIX — "Package Hallucinations: How LLMs Can Invent Vulnerabilities" — https://www.usenix.org/publications/loginonline/we-have-package-you-comprehensive-analysis-package-hallucinations-code
[30] Sciety — "AI-Induced Supply-Chain Compromise: A Systematic Review of Package Hallucinations and Slopsquatting Attacks" — https://sciety.org/articles/activity/10.21203/rs.3.rs-8007192/v1
[31] Rescana — "AI-Hallucinated Dependencies in PyPI and npm: The 2025 Slopsquatting Supply Chain Risk Explained" — https://www.rescana.com/post/ai-hallucinated-dependencies-in-pypi-and-npm-the-2025-slopsquatting-supply-chain-risk-explained
[32] GitGuardian Blog — "Dependency Confusion Attacks and Prevention" — https://blog.gitguardian.com/dependency-confusion-attacks/
[33] Vulert — "CVE-2024-25122: sidekiq-unique-jobs UI server vulnerable to XSS & RCE in Redis" — https://vulert.com/vuln-db/CVE-2024-25122
[34] GitHub Advisory Database — "XSS sidekiq-unique-jobs UI server vulnerability · CVE-2024-25122" — https://github.com/advisories/GHSA-cmh9-rx85-xj38
[35] Pentest-Tools — "Sidekiq < 7.0.8 - Cross-Site Scripting (CVE-2023-1892)" — https://pentest-tools.com/vulnerabilities-exploits/sidekiq-708-cross-site-scripting_22631
[36] Arcjet Blog — "Storing secrets in env vars considered harmful" — https://blog.arcjet.com/storing-secrets-in-env-vars-considered-harmful/
[37] HighVibe — https://highvibe.dev