РАЗБОР Разбор 

SkyForge 1.1.1: пакетный менеджер, whitelist, blacklist и кнопка «Посмеяться» — сделано за 3 часа

S
SkyMonder Newbie · 4 сообщений
Подписаться
23
Режим чтения
Контекст

Неделю назад появился независимый аудит SkyForge — русскоязычного языка программирования, который я пишу. Аудит нашёл 30+ проблем, из них 5 критичных: path traversal в статике, XSS в шаблонах, дефолтный session secret, отсутствие auth на генерируемых CRUD-маршрутах, SSRF в HTTP-клиенте.

Я закрыл всё критичное за 2 часа, выпустил 1.0.8 и написал об этом пост. Но остался вопрос: а что делать, когда кто-то напишет вредоносный пакет для языка? На тот момент системы пакетов вообще не было — только библиотеки в stdlib.

Сегодня, за 3 часа, я спроектировал и выпустил систему пакетов целиком. Реестр на GitHub, whitelist доверенных, blacklist вредоносных, dry-run, show-code, verify, кнопка «Посмеяться» для забаненных пакетов. Ниже — как это работает и почему именно так.


Как работает реестр: GitHub вместо сервера

Не хотел поднимать собственный сервер пакетов — это дорого, требует хостинга и уязвимо для атак. Вместо этого решил использовать GitHub как реестр:

  1. Автор создаёт публичный репозиторий с файлом package.skf в корне
  2. Добавляет топик skyforge-package в настройках репо
  3. Через 1–2 минуты пакет появляется в каталоге
Клиент (SkyForge search) дёргает один запрос к GitHub API:

GET https://api.github.com/search/repositories?q=topic:skyforge-package

Кэш — 1 час в ~/.skyforge/cache/github.json. Без токена GitHub даёт 60 запросов в час, нам хватает с головой.

Что даёт:

  • Zero-хостинг: я плачу только за сервер документации на Render (бесплатный план)
  • Zero-модерации: любой может опубликовать пакет, без approval
  • Zero-регистрации: не нужен аккаунт в «реестре SkyForge», достаточно GitHub
  • Прозрачность: репо публичное, код видно до установки
Что НЕ даёт:

  • Нельзя отозвать пакет (можно только забанить через свой blacklist)
  • Нельзя контролировать версии (берётся default branch репо)
  • GitHub может упасть — тогда реестр недоступен, но кэш спасает

Файл package.skf — манифест на самом языке

Манифест написан на самом SkyForge — это принципиально:

пакет {
имя = "weather"
версия = "1.0.0"
автор = "SkyMonder"
описание = "Погода из open-meteo.com"
репозиторий = "github.com/skymonder/skyforge-weather"
точка_входа = "code/weather.skf"
теги = ["погода", "api", "http"]
лицензия = "MIT"
}

Парсер манифеста — отдельный, не использует лексер SkyForge. Потому что манифест — это метаданные, он не должен меняться при эволюции синтаксиса языка. Простой regex + самописный парсер значений.


Whitelist: доверенные пакеты от автора

Проблема: если любой может опубликовать пакет, как пользователь поймёт, что weather — настоящий пакет от skymonder, а не подделка от злоумышленника?

Решение — whitelist на сервере документации. Файл trusted.json:

{
"version": 1,
"trusted": [
{
"name": "hello",
"repo": "skytech-alt/skyforge-hello",
"author": "SkyMonder",
"note": "Официальный демо-пакет"
}
]
}

Клиент при установке сверяет имя И repo. Если злоумышленник создаст репо skyforge-hello с манифестом имя = "hello", но репо будет villain/skyforge-hello — не совпадёт, пакет не пройдёт как «проверенный». Пользователь увидит жёлтый флаг «Сообщество».


Blacklist: banned.json

Обратная сторона whitelist — список вредоносных пакетов. Файл banned.json на том же сервере:

{
"version": 1,
"banned": [
{
"name": "evil-pkg",
"repo": "example/skyforge-evil-pkg",
"reason": "Кража переменных окружения через HTTP",
"banned_at": "2026-09-13",
"note": "При импорте отправлял os.environ на сторонний сервер"
}
]
}

Если пакет забанен — установка блокируется до скачивания. Проверка идёт в трёх местах:

  1. installer.py — при установке по имени из реестра
  2. cli.py — при установке локального пакета (проверка по манифесту)
  3. cmd_search — забаненные показываются с пометкой [BAN]
Есть аварийный обход для отладки: SKYFORGE_IGNORE_BANNED=1 в переменных окружения. Полезно для тестов.

Особенность: если сервер с banned.json недоступен, клиент использует кэш. Если и кэша нет — fail-open (пропускает установку). Это сознательный выбор: лучше пропустить вредоносный пакет в редком сценарии, чем сломать всем установку при падении сервера.


Кнопка «Посмеяться»

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

Когда пакет забанен — на его странице в каталоге появляется красный блок с причиной бана и большая кнопка:

😈 ПОСМЕЯТЬСЯ

При нажатии:

  1. Играет mp3 со злодейским смехом (<audio> элемент, 115 КБ файл)
  2. Счётчик увеличивается на 1
  3. POST на /api/посмеяться/<имя> — инкремент в памяти сервера
Счётчик — в памяти. При перезапуске Render обнуляется. Для MVP этого достаточно, для продакшена нужен Redis или внешняя БД.

Зачем это? Публичное осмеяние — недооценённый инструмент. Автору вредоносного пакета не «запретят» — его высмеют. Технически это просто счётчик кликов, психологически — мощнее, чем красный флаг «banned». Плюс это даёт пользователям возможность «проголосовать ногами» против плохих пакетов.

Реализация: три строки JS для звука, один маршрут на стороне SkyForge для счётчика. Работает.


Безопасность установки: dry-run, show-code, verify

Три команды, которые делают установку осознанной:

1. --dry-run — показать, что будет установлено:

$ SkyForge install weather --dry-run
DRY-RUN: ничего не установлено. Что будет установлено:
Имя: weather
Версия: 1.0.0
Автор: SkyMonder
Репо: skymonder/skyforge-weather (12 зв.)
Файлов .skf: 4
Размер кода: 3421 байт
Статус: ПРОВЕРЕННЫЙ (в whitelist)

Файлы:
package.skf (280 байт)
code/weather.skf (2841 байт)
...

2. --show-code — скачать и показать код до установки:

$ SkyForge install weather --show-code
Скачиваю weather для просмотра (без установки)...
// === code/weather.skf ===
функция получить_температуру(город) {
...
}

Параноик может прочитать код, потом поставить.

3. SkyForge verify <имя> — проверка целостности после установки:

$ SkyForge verify hello
[OK] Файлы не изменялись с момента установки.
Хеш: sha256:425845ef9f715bce...

При установке считается SHA-256 всех файлов пакета. Если кто-то (или что-то) изменит файлы после установки — verify покажет [WARN] файлы изменены. А SkyForge list автоматически помечает такие пакеты ИЗМЕНЁН!.

Это защита от:

  • supply chain атак (автор подменил содержимое репо после публикации)
  • локальных модификаций (что-то в системе писало в файлы пакета)
  • случайных правок

Почему за 3 часа, а не за 3 недели

Обычно системы пакетов делают месяцами. PyPI, npm, cargo — это годы работы и целые команды. У меня получилось быстро, потому что:

  1. Язык уже был готов — лексер, парсер, интерпретатор, веб-фреймворк. Оставалось только добавить логику поверх.
  2. GitHub API решает половину задач — не нужен свой сервер, своя база, своя аутентификация. Один HTTP-запрос — и у тебя список пакетов.
  3. Я не изобретаю архитектуру — беру то, что уже работает в экосистеме Python: манифест как package.skf, SHA-256 хеши как в pip, dry-run как в apt, whitelist/blacklist как в apt sources.
  4. Аудит задал правильные вопросы — после разбора 30+ дыр в собственном коде я уже думал в категориях «а что если пользователь установит что-то плохое?». Это ускорило дизайн.
Кстати, почему я вообще задумался про безопасность пакетов — потому что неделю назад независимый аудитор нашёл в самом языке 5 критичных дыр. Если бы я не прошёл через этот разбор, я бы, скорее всего, сделал пакетный менеджер без whitelist, blacklist и verify. Аудит научил думать про threat model, и это пригодилось сразу.

Не буду делать вид, что это было легко. Было много итераций: первую версию banned-проверки я сломал, тесты падали на Windows из-за cp1251, mp3 не отдавался через catch-all маршрут. Но всё это уложилось в один день.


Что не вошло в 1.1.1

  • GPG-подписи авторов. Пока только хеши файлов. Если автор сменит содержимое репо — verify это поймает, но не поймает, если это был первый push.
  • Persistent счётчик смеха. In-memory, обнуляется при рестарте.
  • Автоматическое обновление пакетов. Установил --force — переустановил. Никакой семантики версий пока нет.
  • Зависимости. В манифесте есть поле зависимости, но оно игнорируется. Пакет может использовать подтянуть "другой-пакет", но автоматической установки нет.
Всё это — в 1.2.x.


Что стоит попробовать

pip install skyforge-lang==1.1.1

SkyForge search # список пакетов
SkyForge install hello --trust # поставить демо
SkyForge install hello --dry-run # preview без установки
SkyForge install hello --show-code # прочитать код
SkyForge verify hello # проверить хеши

В реестре пока один пакет (hello), но система готова к любым.


Ссылки


Вопрос

Кто-нибудь строил пакетный реестр поверх GitHub? Какие подводные камни вылезли?

Как вы решаете проблему «автор подменил содержимое репо после публикации»? У меня только хеши файлов и whitelist, этого достаточно?

Стоит ли делать собственный сервер пакетов в 1.2, или GitHub как backend — нормальная долгосрочная стратегия?

И если у кого-то есть идея, что ещё стоит забанить через banned.json для демонстрации системы — пишите. Кнопка «Посмеяться» ждёт.
Полезно

Комментарии

0