Search by

yaleksandr89 / archive-guard

yaleksandr89

Security-first inspection and controlled extraction of untrusted ZIP and TAR archives for PHP.

Package info

github.com/yaleksandr89/archive-guard

pkg:composer/yaleksandr89/archive-guard

Fund package maintenance!

Other

Other

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.1.0 2026-09-25 10:25 UTC

This package is auto-updated.

Last update: 2026-09-25 10:31:24 UTC


README

Source Code CI Latest Stable Version Total Downloads PHP Software License

Archive Guard — проверка и извлечение ZIP, TAR и TAR.GZ для PHP

Выберите язык

Русский English Español 中文 Français Deutsch
Выбран English Español 中文 Français Deutsch

Archive Guard — PHP-библиотека для проверки ZIP, TAR и TAR.GZ перед распаковкой и извлечения файлов с заранее заданными ограничениями.

Для чего нужен пакет

Если приложение принимает архив от пользователя или внешнего сервиса, перед распаковкой полезно убедиться, что архив не содержит опасных путей, ссылок и неподдерживаемых элементов, а его размер и объём распакованных данных укладываются в допустимые для приложения пределы.

Пакет позволяет отдельно проверить архив перед распаковкой или сразу проверить и извлечь его содержимое. Если архив не проходит проверку, приложение получает конкретную причину.

Что делает пакет

  • определяет формат по содержимому файла, а не по расширению имени;
  • проверяет пути внутри архива, коллизии после нормализации и совместимость имён с текущей платформой;
  • отклоняет символические и жёсткие ссылки, специальные и неподдерживаемые элементы архива;
  • ограничивает максимальный размер архива, количество элементов, размер отдельного файла и общий объём данных после распаковки;
  • для ZIP проверяет не только метаданные, но и фактическую читаемость содержимого файлов;
  • поддерживает проверку и извлечение зашифрованных ZIP с переданным паролем, если метод шифрования поддерживается текущей средой ZipArchive/libzip;
  • извлекает либо атомарной публикацией в новый каталог, либо слиянием в существующий каталог со стратегией конфликта Reject, Skip или Overwrite;
  • возвращает типизированные результаты и структурированные причины отклонения.

Подробные различия между ZIP, TAR и TAR.GZ описаны в справочнике поддерживаемых архивов.

Требования

  • PHP ^8.4;
  • ext-zip;
  • ext-zlib.

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

Проверка архива

ArchiveGuard::inspect() проверяет архив без извлечения файлов. Ограничения задаются через ArchivePolicy.

Показать пример проверки
<?php

use Yaleksandr\ArchiveGuard\ArchiveGuard;
use Yaleksandr\ArchiveGuard\ArchivePolicy;

$archivePath = '/path/to/archive.zip';

// Подберите ограничения под реальные архивы и ресурсы приложения.
$policy = new ArchivePolicy(
    maxArchiveBytes: 50_000_000,
    maxEntries: 1_000,
    maxEntryUncompressedBytes: 10_000_000,
    maxTotalUncompressedBytes: 100_000_000,
);

$inspection = new ArchiveGuard()->inspect($archivePath, $policy);

if ($inspection->isAccepted()) {
    echo 'Архив прошёл проверку.' . PHP_EOL;
} else {
    foreach ($inspection->violations() as $violation) {
        // code показывает причину отклонения, message — её текстовое описание.
        echo $violation->code->value . ': ' . $violation->message . PHP_EOL;
    }
}

Для зашифрованного ZIP пароль передаётся тем же вызовом:

$inspection = new ArchiveGuard()->inspect(
    $archivePath,
    $policy,
    password: $password,
);

Параметр password используется только для ZIP. TAR и TAR.GZ не предусматривают встроенного парольного шифрования. Передача password для TAR или TAR.GZ вызывает InvalidArgumentException.

Значения ограничений в примере выбраны только для демонстрации. Подбирать их нужно под размер архивов, которые действительно ожидает ваше приложение. Подробный сценарий разобран в руководстве по inspect().

Извлечение файлов

ArchiveGuard::extract() не использует более ранний результат inspect() как разрешение на применение. Текущий архив повторно проверяется в ходе извлечения, а режим задаётся явно через ExtractionOptions.

Показать пример атомарного извлечения
<?php

use Yaleksandr\ArchiveGuard\ArchiveGuard;
use Yaleksandr\ArchiveGuard\ArchivePolicy;
use Yaleksandr\ArchiveGuard\ExtractionOptions;

$archivePath = '/path/to/archive.zip';
$destinationPath = '/path/to/result';

// Финальный каталог назначения для Atomic заранее существовать не должен.
$policy = new ArchivePolicy(
    maxArchiveBytes: 50_000_000,
    maxEntries: 1_000,
    maxEntryUncompressedBytes: 10_000_000,
    maxTotalUncompressedBytes: 100_000_000,
);

$result = new ArchiveGuard()->extract(
    $archivePath,
    $destinationPath,
    $policy,
    ExtractionOptions::atomic(),
);

echo 'Файлов применено: ' . $result->filesExtracted() . PHP_EOL;
echo 'Каталогов создано: ' . $result->directoriesCreated() . PHP_EOL;
echo 'Записано байт: ' . $result->bytesWritten() . PHP_EOL;

Для слияния в уже существующий каталог используйте ExtractionOptions::merge(...) со стратегией Reject, Skip или Overwrite. Результат более раннего вызова inspect() не используется как разрешение на запись: extract() заново использует текущие archivePath, ArchivePolicy и password для проверки архива и отдельно выполняет проверки, зависящие от каталога назначения и выбранного режима.

Подробные контракты Atomic, Merge, блокировки и конфликтов описаны в руководстве по извлечению.

Политика проверки

ArchivePolicy задаёт верхние пределы, после которых архив отклоняется:

  • максимальный размер файла архива;
  • максимальное количество файлов и папок внутри архива; для TAR некоторые служебные элементы формата тоже входят в этот предел;
  • максимальный размер одного файла после распаковки;
  • максимальный суммарный размер всех распакованных данных;
  • необязательный предел отношения распакованного размера к сжатому.

Готовых универсальных значений здесь нет: допустимый архив для загрузки аватара и допустимый архив с резервной копией будут иметь разные пределы. Все параметры и правила их применения описаны в справочнике политики.

Нарушения и ошибки

Если архив распознан, но не проходит заданные проверки, inspect() возвращает InspectionResult со списком нарушений.

Исключения используются для другой ситуации:

  • ArchiveOpenException — файл нельзя открыть, формат не распознан, структура повреждена или принятое незашифрованное ZIP-содержимое нельзя корректно прочитать;
  • ArchiveRejectedException — проверка архива отклонила его; публикация Atomic или применение Merge ещё не начались;
  • ExtractionException — проблема связана с каталогом назначения, блокировкой, подготовкой или публикацией результата либо записью в файловую систему.
Показать пример обработки ошибок
<?php

use Yaleksandr\ArchiveGuard\ArchiveGuard;
use Yaleksandr\ArchiveGuard\ArchivePolicy;
use Yaleksandr\ArchiveGuard\Exception\ArchiveOpenException;
use Yaleksandr\ArchiveGuard\Exception\ArchiveRejectedException;
use Yaleksandr\ArchiveGuard\Exception\ExtractionException;
use Yaleksandr\ArchiveGuard\ExtractionOptions;

$archivePath = '/path/to/archive.zip';
$destinationPath = '/path/to/result';

$policy = new ArchivePolicy(
    maxArchiveBytes: 50_000_000,
    maxEntries: 1_000,
    maxEntryUncompressedBytes: 10_000_000,
    maxTotalUncompressedBytes: 100_000_000,
);

$guard = new ArchiveGuard();

try {
    $result = $guard->extract(
        $archivePath,
        $destinationPath,
        $policy,
        ExtractionOptions::atomic(),
    );
} catch (ArchiveRejectedException $e) {
    foreach ($e->inspectionResult()->violations() as $violation) {
        echo $violation->code->value . PHP_EOL;
    }
} catch (ArchiveOpenException $e) {
    echo $e->getMessage() . PHP_EOL;
} catch (ExtractionException $e) {
    echo $e->getMessage() . PHP_EOL;
}

Полный список кодов нарушений и исключений находится в справочнике ошибок.

Безопасность

Archive Guard проверяет пути и типы элементов, лимиты ArchivePolicy, совместимость имён с текущей платформой и фактическую читаемость ZIP. Библиотека автоматически создаёт временный каталог в родительском каталоге назначения.

Atomic после успешных проверок переименовывает временный каталог в финальный каталог назначения. При ошибке до публикации библиотека удаляет временный каталог. Merge удаляет временный каталог после завершения операции.

Merge предназначен для существующего каталога: архив сначала полностью подготавливается во временном каталоге, затем пакет проверяет конфликты и применяет выбранную стратегию Reject, Skip или Overwrite. Оба режима используют кооперативную блокировку Archive Guard.

Подробная модель проверки и применения результата описана в модели безопасности.

Обратная связь

Если пакет оказался полезен, поставьте звезду на GitHub — так его будет проще найти другим разработчикам. 🤘