/
niceSOFT
/
python3-build
Обзор
Документация
Войти
/
niceSOFT
/
python3-build
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/test_compat_tarfile.py
123 строки
4 KB
Bernát Gábor
♻️ refactor(typing): ban explicit Any across src and tests (#1093)
09 июн 2026, 02:23
Не верифицирован
09 июн 2026, 02:23
1904c77
Код
Авторство
О чём код?
# SPDX-License-Identifier: MIT from __future__ import annotations from collections.abc import Callable from io import BytesIO from pathlib import Path from tarfile import CHRTYPE, LNKTYPE, SYMTYPE, TarError, TarInfo from tarfile import open as tar_open from typing import Protocol import pytest from build._compat.tarfile import _validate_safe_member, safe_extractall FileMember = Callable[[str, bytes], TarInfo] DeviceMember = Callable[[str], TarInfo] ArchiveBuilder = Callable[[Path, 'list[tuple[TarInfo, bytes | None]]'], None] class LinkMember(Protocol): def __call__(self, name: str, linkname: str, *, hard: bool = ...) -> TarInfo: ... def test_safe_extractall_extracts_clean_archive( tmp_path: Path, make_archive: ArchiveBuilder, file_member: FileMember, ) -> None: archive = tmp_path / 'clean.tar' body = b'meta' make_archive(archive, [(file_member('pkg/PKG-INFO', body), body)]) out = tmp_path / 'out' out.mkdir() with tar_open(archive) as tar: safe_extractall(tar, str(out)) assert (out / 'pkg' / 'PKG-INFO').read_bytes() == body def test_validate_safe_member_accepts_clean_file(tmp_path: Path, file_member: FileMember) -> None: base = tmp_path.resolve() _validate_safe_member(file_member('pkg/file.txt', b'x'), base) def test_validate_safe_member_rejects_path_traversal(tmp_path: Path, file_member: FileMember) -> None: base = tmp_path.resolve() with pytest.raises(TarError, match='escapes destination'): _validate_safe_member(file_member('../evil.txt', b'x'), base) def test_validate_safe_member_rejects_absolute_symlink(tmp_path: Path, link_member: LinkMember) -> None: base = tmp_path.resolve() with pytest.raises(TarError, match='link target escapes'): _validate_safe_member(link_member('pkg/evil', '/etc/passwd'), base) def test_validate_safe_member_rejects_relative_escape_symlink(tmp_path: Path, link_member: LinkMember) -> None: base = tmp_path.resolve() with pytest.raises(TarError, match='link target escapes'): _validate_safe_member(link_member('pkg/evil', '../../outside'), base) def test_validate_safe_member_accepts_safe_symlink(tmp_path: Path, link_member: LinkMember) -> None: base = tmp_path.resolve() _validate_safe_member(link_member('pkg/link', 'real.txt'), base) def test_validate_safe_member_rejects_escaping_hardlink(tmp_path: Path, link_member: LinkMember) -> None: base = tmp_path.resolve() with pytest.raises(TarError, match='link target escapes'): _validate_safe_member(link_member('pkg/evil', '../../outside', hard=True), base) def test_validate_safe_member_rejects_device_file(tmp_path: Path, device_member: DeviceMember) -> None: base = tmp_path.resolve() with pytest.raises(TarError, match='special device file'): _validate_safe_member(device_member('pkg/null'), base) @pytest.fixture def make_archive() -> ArchiveBuilder: def _build(path: Path, members: list[tuple[TarInfo, bytes | None]]) -> None: with tar_open(path, 'w') as tar: for info, body in members: tar.addfile(info, BytesIO(body) if body is not None else None) return _build @pytest.fixture def file_member() -> FileMember: def _build(name: str, body: bytes) -> TarInfo: info = TarInfo(name=name) info.size = len(body) return info return _build @pytest.fixture def link_member() -> LinkMember: def _build(name: str, linkname: str, *, hard: bool = False) -> TarInfo: info = TarInfo(name=name) info.type = LNKTYPE if hard else SYMTYPE info.linkname = linkname return info return _build @pytest.fixture def device_member() -> DeviceMember: def _build(name: str) -> TarInfo: info = TarInfo(name=name) info.type = CHRTYPE info.devmajor = 1 info.devminor = 3 return info return _build