ArchiveBox/archivebox/tests/test_permissions.py
2026-08-14 01:53:10 -07:00

192 lines
6.4 KiB
Python

from pathlib import Path
from archivebox.config.permissions import (
is_root_identity,
root_parent_can_grant_group_traversal,
root_should_handoff_data_dir,
select_archivebox_user,
)
def test_root_identity_includes_real_or_effective_root():
assert is_root_identity(0, 0)
assert is_root_identity(0, 911)
assert is_root_identity(1000, 0)
assert not is_root_identity(1000, 1000)
def test_root_uses_archivebox_account_for_root_owned_data_dir():
assert select_archivebox_user(
running_uid=0,
running_gid=0,
effective_uid=0,
effective_gid=0,
sudo_uid=0,
sudo_gid=0,
data_dir_uid=0,
data_dir_gid=0,
account_uid=911,
account_gid=911,
) == (911, 911)
def test_root_preserves_existing_non_root_data_dir_owner():
assert select_archivebox_user(
running_uid=0,
running_gid=0,
effective_uid=0,
effective_gid=0,
sudo_uid=0,
sudo_gid=0,
data_dir_uid=1001,
data_dir_gid=1002,
account_uid=911,
account_gid=911,
) == (1001, 1002)
def test_root_uses_owner_primary_group_for_mixed_user_root_data_dir():
assert select_archivebox_user(
running_uid=0,
running_gid=0,
effective_uid=0,
effective_gid=0,
sudo_uid=1001,
sudo_gid=1002,
data_dir_uid=1001,
data_dir_gid=0,
data_dir_owner_gid=1002,
account_uid=911,
account_gid=911,
) == (1001, 1002)
def test_root_uses_archivebox_account_for_unknown_data_dir_owner():
assert select_archivebox_user(
running_uid=0,
running_gid=0,
effective_uid=0,
effective_gid=0,
sudo_uid=0,
sudo_gid=0,
data_dir_uid=502,
data_dir_gid=20,
account_uid=911,
account_gid=911,
data_dir_owner_exists=False,
) == (911, 911)
def test_non_root_uses_current_effective_identity():
assert select_archivebox_user(
running_uid=501,
running_gid=20,
effective_uid=501,
effective_gid=20,
sudo_uid=0,
sudo_gid=0,
data_dir_uid=0,
data_dir_gid=0,
account_uid=None,
account_gid=None,
) == (501, 20)
def test_effective_root_drops_back_to_real_user():
assert select_archivebox_user(
running_uid=1001,
running_gid=1002,
effective_uid=0,
effective_gid=0,
sudo_uid=0,
sudo_gid=0,
data_dir_uid=0,
data_dir_gid=0,
account_uid=None,
account_gid=None,
) == (1001, 1002)
def test_root_hands_off_root_or_archivebox_owned_collection_boundaries():
assert root_should_handoff_data_dir(is_root=True, data_dir_uid=0, account_uid=911)
assert root_should_handoff_data_dir(is_root=True, data_dir_uid=911, account_uid=911)
assert not root_should_handoff_data_dir(is_root=True, data_dir_uid=1001, account_uid=911)
assert not root_should_handoff_data_dir(is_root=False, data_dir_uid=911, account_uid=911)
assert not root_should_handoff_data_dir(is_root=True, data_dir_uid=911, account_uid=None)
assert root_should_handoff_data_dir(
is_root=True,
data_dir_uid=502,
account_uid=911,
data_dir_owner_exists=False,
)
def test_root_setup_commands_hand_off_only_an_empty_data_dir(tmp_path):
from archivebox.config.permissions import root_data_dir_handoff_paths
setup_commands = (
["archivebox", "init"],
["archivebox", "install"],
["archivebox", "server", "--init"],
["archivebox", "server", "--quick-init"],
["archivebox", "add", "--init", "https://example.com"],
)
for argv in setup_commands:
assert root_data_dir_handoff_paths(tmp_path, argv) == (tmp_path,)
unrelated = tmp_path / "unrelated.txt"
unrelated.write_text("keep root ownership")
for argv in setup_commands:
assert root_data_dir_handoff_paths(tmp_path, argv) == ()
def test_existing_collection_handoff_is_bounded_to_known_top_level_paths(tmp_path):
from archivebox.config.permissions import root_data_dir_handoff_paths
database = tmp_path / "index.sqlite3"
archive = tmp_path / "archive"
custom_plugins = tmp_path / "custom_plugins"
custom_templates = tmp_path / "custom_templates"
nested = archive / "large-existing-snapshot"
errors_log = tmp_path / "logs" / "errors.log"
database.touch()
nested.mkdir(parents=True)
custom_plugins.mkdir()
custom_templates.mkdir()
errors_log.parent.mkdir()
errors_log.touch()
paths = root_data_dir_handoff_paths(tmp_path, ["archivebox", "status"])
assert paths == (tmp_path, database, archive, custom_plugins, custom_templates, errors_log.parent, errors_log)
assert nested not in paths
assert all(path == tmp_path or path.parent in (tmp_path, errors_log.parent) for path in paths)
def test_permission_repairs_avoid_recursive_collection_and_abxpkg_chown():
from archivebox.misc import checks
assert "chown -R" not in Path(checks.__file__).read_text(encoding="utf-8")
entrypoint = (Path(__file__).parents[2] / "bin" / "docker_entrypoint.sh").read_text(encoding="utf-8")
permission_error = entrypoint.partition("permission_error() {")[2].partition("\n}")[0]
abxpkg_repairs = entrypoint.partition('ensure_dir "$ABXPKG_LIB_DIR"')[2].partition("run_as_archivebox touch")[0]
assert 'for package_dir in "$provider_dir"/packages/*; do' in abxpkg_repairs
assert 'ensure_file_owner "$package_dir/derived.env"' in abxpkg_repairs
assert "chown -R" not in permission_error
assert "chown -R" not in abxpkg_repairs
def test_root_handoff_never_selects_filesystem_root():
from archivebox.config.permissions import root_data_dir_handoff_paths
assert root_data_dir_handoff_paths(Path("/"), ["archivebox", "init"]) == ()
def test_root_private_parent_grants_only_archivebox_group_traversal():
assert root_parent_can_grant_group_traversal(parent_uid=0, parent_gid=0, parent_mode=0o700, account_gid=911)
assert not root_parent_can_grant_group_traversal(parent_uid=0, parent_gid=0, parent_mode=0o701, account_gid=911)
assert not root_parent_can_grant_group_traversal(parent_uid=0, parent_gid=911, parent_mode=0o710, account_gid=911)
assert not root_parent_can_grant_group_traversal(parent_uid=0, parent_gid=100, parent_mode=0o750, account_gid=911)
assert not root_parent_can_grant_group_traversal(parent_uid=1000, parent_gid=1000, parent_mode=0o700, account_gid=911)