Skip to content
 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ospx-tools

Чтение пакетов .ospx и манифестов opm-metadata.xml для OneScript: разбор и запись манифеста, безопасное извлечение содержимого пакета и валидация. Библиотека рассчитана на приёмную сторону — хаб, зеркало, линтер, — которая разбирает чужой архив и не имеет права ему верить.

Публичный интерфейс — один модуль-фасад ПакетыOspx. Модель манифеста, парсер, сериализатор, читатель и валидатор лежат в src/internal и меняются без оглядки на обратную совместимость.

Пакет = ПакетыOspx.ПрочитатьПакет("semver-1.1.1.ospx");   // манифест + README + файлы
Итог  = ПакетыOspx.ПроверитьПакет("подозрительный.ospx"); // разбор без исключений

Зависимостей времени выполнения нет — только стандартная библиотека OneScript.

Установка

opm install ospx-tools
.ЗависитОт("ospx-tools", "0.2.0")

Формат .ospx

Zip-архив, в корне которого лежат ровно два файла:

  • opm-metadata.xml — манифест пакета (пространство имён http://oscript.io/schemas/opm-metadata/1.0): элементы name, version, author, author-email, description, repo-address, engine-version, manifest-version, повторяющиеся <depends-on name="…" version="…" version-max="…" dev="true|false"/> и <executable>путь</executable>;
  • content.zip — вложенный zip с файлами пакета: lib.config в корне, исходники и, если автор включил его в поставку, README.md.

Быстрый старт

#Использовать ospx-tools

Пакет = ПакетыOspx.ПрочитатьПакет("semver-1.1.1.ospx");
Сообщить(Пакет.Манифест().Имя());      // semver
Сообщить(Пакет.Манифест().Версия());   // 1.1.1
Если Пакет.ЕстьREADME() Тогда
    Сообщить(Пакет.ТекстREADME());
КонецЕсли;

Итог = ПакетыOspx.ПроверитьПакет("подозрительный.ospx");
Если Не Итог.Корректен() Тогда
    Сообщить(Итог.ОписаниеОшибок());
КонецЕсли;

Чтение бросает исключение на любом отказе, проверка — не бросает никогда: ПроверитьПакет ловит отказ чтения и кладёт его в список ошибок. Первое — для своих файлов, второе — для чужих.

API

Метод Назначение
ПрочитатьПакет(Путь, Ограничения = Неопределено) манифест, README и список файлов content.zip
ПрочитатьМанифестПакета(Путь, Ограничения = Неопределено) только манифест — content.zip не разбирается
НовыеОграничения() лимиты распаковки со значениями по умолчанию
НовыйМанифест(Имя = "", Версия = "") пустой манифест
ПрочитатьМанифестИзСтроки(ТекстXML) разбор opm-metadata.xml из строки (BOM допускается)
ЗаписатьМанифестВСтроку(Манифест) сериализация в XML формата opm
ЗаписатьМанифестВФайл(Манифест, Путь) то же в файл, UTF-8
ПроверитьПакет(Путь, Ограничения = Неопределено) целостность архива + корректность манифеста
ПроверитьМанифест(Манифест) обязательные поля, имена, версии, дубли зависимостей
ИмяПакетаКорректно(Имя) латиница, цифры, -, _, .; первый символ — буква или цифра; до 100 символов
ВерсияПакетаКорректна(Версия) semver до 4 числовых компонент, prerelease/build, SNAPSHOT

Что отдаёт фасад

Пакет (ПрочитатьПакет): Манифест(), ЕстьREADME(), ТекстREADME(), Файлы() — пути внутри content.zip.

Манифест: Имя(), Версия(), Автор(), АдресАвтора(), Описание(), АдресРепозитория(), ВерсияДвижка(), ВерсияМанифеста(), Зависимости(), Зависимость(Имя) — поиск без учёта регистра, ИсполняемыеФайлы(). Зависимость: Имя(), Версия(), МаксимальнаяВерсия(), ДляРазработки().

Методы Установить… и Добавить… возвращают сам манифест и допускают цепочки:

Манифест = ПакетыOspx.НовыйМанифест("mylib", "0.1.0")
    .УстановитьОписание("Моя библиотека")
    .УстановитьВерсиюДвижка("2.0.0")
    .ДобавитьЗависимость("logos", "1.7.1")
    .ДобавитьЗависимость("asserts", "1.6.0", , Истина);   // dev-зависимость

XML = ПакетыOspx.ЗаписатьМанифестВСтроку(Манифест);

Результат проверки: Корректен(), Ошибки() — массив строк, ОписаниеОшибок() — все ошибки одной строкой. Проверка собирает все найденные ошибки, а не останавливается на первой.

Ограничения распаковки

Ограничения = ПакетыOspx.НовыеОграничения()
    .УстановитьМаксимальныйОбщийРазмер(10 * 1024 * 1024)
    .УстановитьМаксимальноеКоличествоФайлов(500);

Пакет = ПакетыOspx.ПрочитатьПакет("подозрительный.ospx", Ограничения);
Лимит По умолчанию Метод
Суммарный размер распакованного 256 МБ УстановитьМаксимальныйОбщийРазмер
Количество файлов в архиве 10 000 УстановитьМаксимальноеКоличествоФайлов
Глубина путей внутри content.zip 32 сегмента УстановитьМаксимальнуюГлубинуПутей
Размер opm-metadata.xml 1 МБ УстановитьМаксимальныйРазмерМанифеста
Размер README.md 2 МБ УстановитьМаксимальныйРазмерREADME
Потолок транзиторной записи на диск 256 МБ УстановитьМаксимальнуюТранзиторнуюЗапись

Превышение любого лимита прерывает чтение исключением «Превышено ограничение распаковки: …».

Модель угроз

Заявленным размерам из central directory библиотека не доверяет: их пишет тот, кто собрал архив. Защита работает эшелонами:

  1. До извлечения — гейты по заявленному размеру, количеству файлов и глубине путей. Отсекают честно заявленную бомбу.
  2. Перед извлечением — гейт по сжатому размеру записи. Сжатый размер занизить нельзя: он физически ограничен размером самого файла. Для манифеста и README требуется сжатый размер × 1032 (физический максимум расширения deflate) <= МаксимальнаяТранзиторнаяЗапись. Гейт ограничивает возможный объём записи на диск, а не допуск файла, — поэтому честно сжатые большие файлы проходят, а решение о допуске принимает шаг 3. Для content.zip сжатый размер не должен превышать общий лимит.
  3. После извлечения — сверка фактического размера с лимитом, fail-closed: отсутствие извлечённого файла — тоже ошибка. Временный каталог вычищается всегда: и на успехе, и на любом отказе.

Дополнительно читатель отвергает небезопасные пути записей (../, абсолютные — zip-slip), архивы zip64 и многотомные, а также дубли имён записей в central directory: такой архив неоднозначен — разные распаковщики возьмут разные копии. Собственный разбор оглавления сверяется с числом записей, которое видит zip-библиотека, — защита от parser differential.

Вложенные в content.zip архивы не распаковываются: кроме README, записи content.zip на диск вообще не извлекаются, поэтому их лживые размеры вреда не наносят.

Остаточный риск. У самого content.zip — уже сжатого архива, типичный коэффициент повторного сжатия ~1 — фактический объём распаковки известен только после извлечения. Транзиторная запись во временный каталог ограничена физическим пределом deflate и немедленно вычищается при отказе. Принимающей стороне рекомендуется дополнительно ограничивать размер загружаемого файла.

Архитектура

Наружу выставлен только фасад; всё, что ниже, — деталь реализации:

src/Модули/ПакетыOspx.os      публичный фасад (#Использовать "../internal")
src/internal/Классы/          ЧитательOspx, ВалидаторПакета, ПарсерМанифеста,
                              СериализаторМанифеста, МанифестПакета,
                              ЗависимостьПакета, ПакетOspx,
                              ОграниченияРаспаковки, РезультатВалидации

Тесты

oneunit execute

70 тестов на 7 наборов. Фикстуры в tests/fixtures — настоящие пакеты с hub.oscript.io (semver, logos, asserts) и специально подготовленные некорректные архивы: битые, без манифеста, zip-бомбы, zip-slip, лгущие о размерах, с дублями записей. Тестам внутренностей доступен #Использовать "../src/internal".

Набор Тесты_ЗахватИмён держит границу фасада: соседняя библиотека делает глобальным модуль с распространённым именем (как oscript-yamlСериализатор), после чего дочерний oscript вызывает все методы фасада.

Лицензия

MIT — см. LICENSE.

About

Чтение пакетов .ospx и манифестов opm-metadata.xml для OneScript: разбор, запись, валидация, защита от zip-бомб

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages