Modify the function able to handle messages about skipped binary fcontext files and skip them. This started to happen on c10s. Extend the unit test to cover this new scenario. Signed-off-by: Tomáš Hozza <thozza@redhat.com>
339 lines
14 KiB
Python
339 lines
14 KiB
Python
import os
|
|
import subprocess
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from osbuild.testutil import make_fake_tree
|
|
from osbuild.testutil.imports import import_module_from_path
|
|
|
|
osbuild_image_info = import_module_from_path("osbuild_image_info", "tools/osbuild-image-info")
|
|
|
|
|
|
@pytest.mark.parametrize("fake_tree,entries", (
|
|
# no entries
|
|
({}, []),
|
|
# one entry
|
|
(
|
|
{
|
|
"/boot/loader/entries/0649288e52434223afde4c36460a375e-6.11.9-100.fc39.x86_64.conf": """title Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)
|
|
version 6.11.9-100.fc39.x86_64
|
|
linux /boot/vmlinuz-6.11.9-100.fc39.x86_64
|
|
initrd /boot/initramfs-6.11.9-100.fc39.x86_64.img
|
|
options root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8
|
|
grub_users $grub_users
|
|
grub_arg --unrestricted
|
|
grub_class fedora""",
|
|
},
|
|
[
|
|
{
|
|
"title": "Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)",
|
|
"version": "6.11.9-100.fc39.x86_64",
|
|
"linux": "/boot/vmlinuz-6.11.9-100.fc39.x86_64",
|
|
"initrd": "/boot/initramfs-6.11.9-100.fc39.x86_64.img",
|
|
"options": "root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8",
|
|
"grub_users": "$grub_users",
|
|
"grub_arg": "--unrestricted",
|
|
"grub_class": "fedora",
|
|
},
|
|
]
|
|
),
|
|
# two entries
|
|
(
|
|
{
|
|
"/boot/loader/entries/0649288e52434223afde4c36460a375e-6.11.9-100.fc39.x86_64.conf": """title Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)
|
|
version 6.11.9-100.fc39.x86_64
|
|
linux /boot/vmlinuz-6.11.9-100.fc39.x86_64
|
|
initrd /boot/initramfs-6.11.9-100.fc39.x86_64.img
|
|
options root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8
|
|
grub_users $grub_users
|
|
grub_arg --unrestricted
|
|
grub_class fedora""",
|
|
"/boot/loader/entries/0649288e52434223afde4c36460a375e-6.11.9-101.fc39.x86_64.conf": """title Fedora Linux (6.11.9-101.fc39.x86_64) 39 (Thirty Nine)
|
|
version 6.11.9-101.fc39.x86_64
|
|
linux /boot/vmlinuz-6.11.9-101.fc39.x86_64
|
|
initrd /boot/initramfs-6.11.9-101.fc39.x86_64.img
|
|
options root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8
|
|
grub_users $grub_users
|
|
grub_arg --unrestricted
|
|
grub_class fedora""",
|
|
},
|
|
[
|
|
{
|
|
"title": "Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)",
|
|
"version": "6.11.9-100.fc39.x86_64",
|
|
"linux": "/boot/vmlinuz-6.11.9-100.fc39.x86_64",
|
|
"initrd": "/boot/initramfs-6.11.9-100.fc39.x86_64.img",
|
|
"options": "root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8",
|
|
"grub_users": "$grub_users",
|
|
"grub_arg": "--unrestricted",
|
|
"grub_class": "fedora",
|
|
},
|
|
{
|
|
"title": "Fedora Linux (6.11.9-101.fc39.x86_64) 39 (Thirty Nine)",
|
|
"version": "6.11.9-101.fc39.x86_64",
|
|
"linux": "/boot/vmlinuz-6.11.9-101.fc39.x86_64",
|
|
"initrd": "/boot/initramfs-6.11.9-101.fc39.x86_64.img",
|
|
"options": "root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8",
|
|
"grub_users": "$grub_users",
|
|
"grub_arg": "--unrestricted",
|
|
"grub_class": "fedora",
|
|
},
|
|
]
|
|
),
|
|
# one entry with extra newlines
|
|
(
|
|
{
|
|
"/boot/loader/entries/0649288e52434223afde4c36460a375e-6.11.9-100.fc39.x86_64.conf": """title Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)
|
|
version 6.11.9-100.fc39.x86_64
|
|
linux /boot/vmlinuz-6.11.9-100.fc39.x86_64
|
|
initrd /boot/initramfs-6.11.9-100.fc39.x86_64.img
|
|
options root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8
|
|
grub_users $grub_users
|
|
grub_arg --unrestricted
|
|
grub_class fedora
|
|
|
|
""",
|
|
},
|
|
[
|
|
{
|
|
"title": "Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)",
|
|
"version": "6.11.9-100.fc39.x86_64",
|
|
"linux": "/boot/vmlinuz-6.11.9-100.fc39.x86_64",
|
|
"initrd": "/boot/initramfs-6.11.9-100.fc39.x86_64.img",
|
|
"options": "root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8",
|
|
"grub_users": "$grub_users",
|
|
"grub_arg": "--unrestricted",
|
|
"grub_class": "fedora",
|
|
},
|
|
]
|
|
),
|
|
# one entry with comments
|
|
(
|
|
{
|
|
"/boot/loader/entries/0649288e52434223afde4c36460a375e-6.11.9-100.fc39.x86_64.conf": """title Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)
|
|
# this is a very useful comment
|
|
version 6.11.9-100.fc39.x86_64
|
|
linux /boot/vmlinuz-6.11.9-100.fc39.x86_64
|
|
initrd /boot/initramfs-6.11.9-100.fc39.x86_64.img
|
|
options root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8
|
|
# this is another very useful comment
|
|
grub_users $grub_users
|
|
grub_arg --unrestricted
|
|
grub_class fedora""",
|
|
},
|
|
[
|
|
{
|
|
"title": "Fedora Linux (6.11.9-100.fc39.x86_64) 39 (Thirty Nine)",
|
|
"version": "6.11.9-100.fc39.x86_64",
|
|
"linux": "/boot/vmlinuz-6.11.9-100.fc39.x86_64",
|
|
"initrd": "/boot/initramfs-6.11.9-100.fc39.x86_64.img",
|
|
"options": "root=UUID=a7e970a5-14fb-4a8a-ab09-603d1ac3fee9 ro crashkernel=auto net.ifnames=0 rhgb console=tty0 console=ttyS0,115200n8",
|
|
"grub_users": "$grub_users",
|
|
"grub_arg": "--unrestricted",
|
|
"grub_class": "fedora",
|
|
},
|
|
]
|
|
),
|
|
))
|
|
def test_read_boot_entries(tmp_path, fake_tree, entries):
|
|
make_fake_tree(tmp_path, fake_tree)
|
|
assert osbuild_image_info.read_boot_entries(tmp_path / "boot") == entries
|
|
|
|
|
|
def test_read_default_target_ok(tmp_path):
|
|
"""
|
|
Test the happy case when determinig the systemd default target
|
|
"""
|
|
make_fake_tree(tmp_path, {
|
|
"/usr/lib/systemd/system/multi-user.target": """# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
#
|
|
# This file is part of systemd.
|
|
#
|
|
# systemd is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation; either version 2.1 of the License, or
|
|
# (at your option) any later version.
|
|
|
|
[Unit]
|
|
Description=Multi-User System
|
|
Documentation=man:systemd.special(7)
|
|
Requires=basic.target
|
|
Conflicts=rescue.service rescue.target
|
|
After=basic.target rescue.service rescue.target
|
|
AllowIsolate=yes
|
|
"""
|
|
})
|
|
etc_systemd_system_dir = tmp_path / "etc/systemd/system"
|
|
etc_systemd_system_dir.mkdir(parents=True)
|
|
default_target_link = etc_systemd_system_dir / "default.target"
|
|
default_target_link.symlink_to("/usr/lib/systemd/system/multi-user.target")
|
|
|
|
assert osbuild_image_info.read_default_target(tmp_path) == "multi-user.target"
|
|
|
|
|
|
def test_read_default_target_none(tmp_path):
|
|
"""
|
|
Test the case when when there is no default target set on the system
|
|
"""
|
|
assert osbuild_image_info.read_default_target(tmp_path) == ""
|
|
|
|
|
|
# root is needed, because the script will bind mount the dir as read-only
|
|
@pytest.mark.skipif(os.getuid() != 0, reason="root only")
|
|
def test_empty_report_fail(tmp_path):
|
|
"""
|
|
Test that the main() exits with a non-zero exit code if the report is empty.
|
|
"""
|
|
with pytest.raises(SystemExit) as e, patch("sys.argv", ["osbuild-image-info", str(tmp_path)]):
|
|
osbuild_image_info.main()
|
|
assert e.value.code == 1
|
|
|
|
|
|
def make_fake_iso(iso_tree, output_dir) -> str:
|
|
iso_path = os.path.join(output_dir, "image.iso")
|
|
subprocess.run(["mkisofs", "-o", iso_path, "-R", "-J", iso_tree], check=True)
|
|
return iso_path
|
|
|
|
|
|
@pytest.mark.skipif(os.getuid() != 0, reason="root only")
|
|
def test_analyse_iso_fail_mount(tmp_path):
|
|
# fake ISO that can't be mounted
|
|
image_path = tmp_path / "image.iso"
|
|
image_path.touch()
|
|
|
|
with pytest.raises(
|
|
subprocess.CalledProcessError,
|
|
match=fr"^Command '\['mount', '-o', 'ro,loop', PosixPath\('{image_path}'\)"):
|
|
osbuild_image_info.analyse_iso(image_path)
|
|
|
|
|
|
@pytest.mark.skipif(os.getuid() != 0, reason="root only")
|
|
def test_analyse_iso_fail_no_tarball(tmp_path):
|
|
# ISO that can be mounted, but doesn't contain the liveimg.tar.gz
|
|
iso_tree = tmp_path / "iso_tree"
|
|
iso_tree.mkdir()
|
|
# NB: The random file is added to the ISO, because in GH actions, the produced
|
|
# ISO was not valid and was consistently failing to be mounted.
|
|
random_file = iso_tree / "random_file"
|
|
random_file.write_text("random content")
|
|
|
|
image_path = make_fake_iso(iso_tree, tmp_path)
|
|
|
|
with pytest.raises(
|
|
subprocess.CalledProcessError,
|
|
match=r"^Command '\['tar', '--selinux', '--xattrs', '--acls', '-x', '--auto-compress', '-f', '/tmp/\w+/liveimg.tar.gz"):
|
|
osbuild_image_info.analyse_iso(image_path)
|
|
|
|
|
|
@pytest.mark.parametrize("subprocess_output,expected_report", [
|
|
pytest.param(
|
|
"""Would relabel {tmp_path}/etc/shells from unconfined_u:object_r:etc_t:s0 to system_u:object_r:etc_t:s0
|
|
Would relabel {tmp_path}/etc/ld.so.cache from unconfined_u:object_r:ld_so_cache_t:s0 to system_u:object_r:ld_so_cache_t:s0
|
|
Would relabel {tmp_path}/etc/alternatives/roff.7.gz from unconfined_u:object_r:etc_t:s0 to system_u:object_r:etc_t:s0
|
|
Would relabel {tmp_path}/var/lib/selinux/targeted/active from unconfined_u:object_r:semanage_store_t:s0 to system_u:object_r:semanage_store_t:s0
|
|
Would relabel {tmp_path}/var/lib/alternatives/roff.7.gz from unconfined_u:object_r:rpm_var_lib_t:s0 to system_u:object_r:rpm_var_lib_t:s0
|
|
""",
|
|
[
|
|
{
|
|
"filename": "/etc/alternatives/roff.7.gz",
|
|
"actual": "unconfined_u:object_r:etc_t:s0",
|
|
"expected": "system_u:object_r:etc_t:s0",
|
|
},
|
|
{
|
|
"filename": "/etc/ld.so.cache",
|
|
"actual": "unconfined_u:object_r:ld_so_cache_t:s0",
|
|
"expected": "system_u:object_r:ld_so_cache_t:s0",
|
|
},
|
|
{
|
|
"filename": "/etc/shells",
|
|
"actual": "unconfined_u:object_r:etc_t:s0",
|
|
"expected": "system_u:object_r:etc_t:s0",
|
|
},
|
|
{
|
|
"filename": "/var/lib/alternatives/roff.7.gz",
|
|
"actual": "unconfined_u:object_r:rpm_var_lib_t:s0",
|
|
"expected": "system_u:object_r:rpm_var_lib_t:s0",
|
|
},
|
|
{
|
|
"filename": "/var/lib/selinux/targeted/active",
|
|
"actual": "unconfined_u:object_r:semanage_store_t:s0",
|
|
"expected": "system_u:object_r:semanage_store_t:s0",
|
|
},
|
|
],
|
|
id="happy case",
|
|
),
|
|
pytest.param(
|
|
"",
|
|
[],
|
|
id="empty",
|
|
),
|
|
pytest.param(
|
|
"""{tmp_path}/etc/selinux/targeted/contexts/files/file_contexts.bin: Old compiled fcontext format, skipping
|
|
{tmp_path}/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin: Old compiled fcontext format, skipping
|
|
""",
|
|
[],
|
|
id="only lines to skip",
|
|
),
|
|
pytest.param(
|
|
"""{tmp_path}/etc/selinux/targeted/contexts/files/file_contexts.bin: Old compiled fcontext format, skipping
|
|
{tmp_path}/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin: Old compiled fcontext format, skipping
|
|
|
|
Would relabel {tmp_path}/etc/shells from unconfined_u:object_r:etc_t:s0 to system_u:object_r:etc_t:s0
|
|
Would relabel {tmp_path}/etc/ld.so.cache from unconfined_u:object_r:ld_so_cache_t:s0 to system_u:object_r:ld_so_cache_t:s0
|
|
Would relabel {tmp_path}/etc/alternatives/roff.7.gz from unconfined_u:object_r:etc_t:s0 to system_u:object_r:etc_t:s0
|
|
Would relabel {tmp_path}/var/lib/selinux/targeted/active from unconfined_u:object_r:semanage_store_t:s0 to system_u:object_r:semanage_store_t:s0
|
|
Would relabel {tmp_path}/var/lib/alternatives/roff.7.gz from unconfined_u:object_r:rpm_var_lib_t:s0 to system_u:object_r:rpm_var_lib_t:s0
|
|
""",
|
|
[
|
|
{
|
|
"filename": "/etc/alternatives/roff.7.gz",
|
|
"actual": "unconfined_u:object_r:etc_t:s0",
|
|
"expected": "system_u:object_r:etc_t:s0",
|
|
},
|
|
{
|
|
"filename": "/etc/ld.so.cache",
|
|
"actual": "unconfined_u:object_r:ld_so_cache_t:s0",
|
|
"expected": "system_u:object_r:ld_so_cache_t:s0",
|
|
},
|
|
{
|
|
"filename": "/etc/shells",
|
|
"actual": "unconfined_u:object_r:etc_t:s0",
|
|
"expected": "system_u:object_r:etc_t:s0",
|
|
},
|
|
{
|
|
"filename": "/var/lib/alternatives/roff.7.gz",
|
|
"actual": "unconfined_u:object_r:rpm_var_lib_t:s0",
|
|
"expected": "system_u:object_r:rpm_var_lib_t:s0",
|
|
},
|
|
{
|
|
"filename": "/var/lib/selinux/targeted/active",
|
|
"actual": "unconfined_u:object_r:semanage_store_t:s0",
|
|
"expected": "system_u:object_r:semanage_store_t:s0",
|
|
},
|
|
],
|
|
id="valid lines mixed with lines to skip",
|
|
)
|
|
])
|
|
def test_read_selinux_ctx_mismatch(tmp_path, subprocess_output, expected_report):
|
|
"""
|
|
Test the read_selinux_ctx_mismatch function
|
|
"""
|
|
policy_dir = tmp_path / "etc/selinux/targeted/policy"
|
|
policy_dir.mkdir(parents=True)
|
|
policy_file = policy_dir / "policy.33"
|
|
policy_file.touch()
|
|
|
|
with patch("subprocess.check_output") as subprocess_check_output:
|
|
subprocess_check_output.return_value = subprocess_output.format(tmp_path=tmp_path)
|
|
report = osbuild_image_info.read_selinux_ctx_mismatch(tmp_path.as_posix(), False)
|
|
|
|
assert subprocess_check_output.call_count == 1
|
|
assert subprocess_check_output.call_args[0][0] == [
|
|
"setfiles", "-r", tmp_path.as_posix(),
|
|
"-nvF",
|
|
"-c", os.fspath(tmp_path / "etc/selinux/targeted/policy/policy.33"),
|
|
os.fspath(tmp_path / "etc/selinux/targeted/contexts/files/file_contexts"),
|
|
tmp_path.as_posix(),
|
|
]
|
|
assert report == expected_report
|